]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Demote SSH bypass-auth option; downplay in docs.
[PuTTY.git] / config.c
1 /*
2  * config.c - the platform-independent parts of the PuTTY
3  * configuration box.
4  */
5
6 #include <assert.h>
7 #include <stdlib.h>
8
9 #include "putty.h"
10 #include "dialog.h"
11 #include "storage.h"
12
13 #define PRINTER_DISABLED_STRING "None (printing disabled)"
14
15 #define HOST_BOX_TITLE "Host Name (or IP address)"
16 #define PORT_BOX_TITLE "Port"
17
18 void conf_radiobutton_handler(union control *ctrl, void *dlg,
19                               void *data, int event)
20 {
21     int button;
22     Conf *conf = (Conf *)data;
23
24     /*
25      * For a standard radio button set, the context parameter gives
26      * the primary key (CONF_foo), and the extra data per button
27      * gives the value the target field should take if that button
28      * is the one selected.
29      */
30     if (event == EVENT_REFRESH) {
31         int val = conf_get_int(conf, ctrl->radio.context.i);
32         for (button = 0; button < ctrl->radio.nbuttons; button++)
33             if (val == ctrl->radio.buttondata[button].i)
34                 break;
35         /* We expected that `break' to happen, in all circumstances. */
36         assert(button < ctrl->radio.nbuttons);
37         dlg_radiobutton_set(ctrl, dlg, button);
38     } else if (event == EVENT_VALCHANGE) {
39         button = dlg_radiobutton_get(ctrl, dlg);
40         assert(button >= 0 && button < ctrl->radio.nbuttons);
41         conf_set_int(conf, ctrl->radio.context.i,
42                      ctrl->radio.buttondata[button].i);
43     }
44 }
45
46 #define CHECKBOX_INVERT (1<<30)
47 void conf_checkbox_handler(union control *ctrl, void *dlg,
48                            void *data, int event)
49 {
50     int key, invert;
51     Conf *conf = (Conf *)data;
52
53     /*
54      * For a standard checkbox, the context parameter gives the
55      * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
56      */
57     key = ctrl->checkbox.context.i;
58     if (key & CHECKBOX_INVERT) {
59         key &= ~CHECKBOX_INVERT;
60         invert = 1;
61     } else
62         invert = 0;
63
64     /*
65      * C lacks a logical XOR, so the following code uses the idiom
66      * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
67      * iff exactly one of a and b is nonzero, otherwise 0.)
68      */
69
70     if (event == EVENT_REFRESH) {
71         int val = conf_get_int(conf, key);
72         dlg_checkbox_set(ctrl, dlg, (!val ^ !invert));
73     } else if (event == EVENT_VALCHANGE) {
74         conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert);
75     }
76 }
77
78 void conf_editbox_handler(union control *ctrl, void *dlg,
79                           void *data, int event)
80 {
81     /*
82      * The standard edit-box handler expects the main `context'
83      * field to contain the primary key. The secondary `context2'
84      * field indicates the type of this field:
85      *
86      *  - if context2 > 0, the field is a string.
87      *  - if context2 == -1, the field is an int and the edit box
88      *    is numeric.
89      *  - if context2 < -1, the field is an int and the edit box is
90      *    _floating_, and (-context2) gives the scale. (E.g. if
91      *    context2 == -1000, then typing 1.2 into the box will set
92      *    the field to 1200.)
93      */
94     int key = ctrl->editbox.context.i;
95     int length = ctrl->editbox.context2.i;
96     Conf *conf = (Conf *)data;
97
98     if (length > 0) {
99         if (event == EVENT_REFRESH) {
100             char *field = conf_get_str(conf, key);
101             dlg_editbox_set(ctrl, dlg, field);
102         } else if (event == EVENT_VALCHANGE) {
103             char *field = dlg_editbox_get(ctrl, dlg);
104             conf_set_str(conf, key, field);
105             sfree(field);
106         }
107     } else if (length < 0) {
108         if (event == EVENT_REFRESH) {
109             char str[80];
110             int value = conf_get_int(conf, key);
111             if (length == -1)
112                 sprintf(str, "%d", value);
113             else
114                 sprintf(str, "%g", (double)value / (double)(-length));
115             dlg_editbox_set(ctrl, dlg, str);
116         } else if (event == EVENT_VALCHANGE) {
117             char *str = dlg_editbox_get(ctrl, dlg);
118             if (length == -1)
119                 conf_set_int(conf, key, atoi(str));
120             else
121                 conf_set_int(conf, key, (int)((-length) * atof(str)));
122             sfree(str);
123         }
124     }
125 }
126
127 void conf_filesel_handler(union control *ctrl, void *dlg,
128                           void *data, int event)
129 {
130     int key = ctrl->fileselect.context.i;
131     Conf *conf = (Conf *)data;
132
133     if (event == EVENT_REFRESH) {
134         dlg_filesel_set(ctrl, dlg, conf_get_filename(conf, key));
135     } else if (event == EVENT_VALCHANGE) {
136         Filename *filename = dlg_filesel_get(ctrl, dlg);
137         conf_set_filename(conf, key, filename);
138         filename_free(filename);
139     }
140 }
141
142 void conf_fontsel_handler(union control *ctrl, void *dlg,
143                           void *data, int event)
144 {
145     int key = ctrl->fontselect.context.i;
146     Conf *conf = (Conf *)data;
147
148     if (event == EVENT_REFRESH) {
149         dlg_fontsel_set(ctrl, dlg, conf_get_fontspec(conf, key));
150     } else if (event == EVENT_VALCHANGE) {
151         FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg);
152         conf_set_fontspec(conf, key, fontspec);
153         fontspec_free(fontspec);
154     }
155 }
156
157 static void config_host_handler(union control *ctrl, void *dlg,
158                                 void *data, int event)
159 {
160     Conf *conf = (Conf *)data;
161
162     /*
163      * This function works just like the standard edit box handler,
164      * only it has to choose the control's label and text from two
165      * different places depending on the protocol.
166      */
167     if (event == EVENT_REFRESH) {
168         if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
169             /*
170              * This label text is carefully chosen to contain an n,
171              * since that's the shortcut for the host name control.
172              */
173             dlg_label_change(ctrl, dlg, "Serial line");
174             dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
175         } else {
176             dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
177             dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
178         }
179     } else if (event == EVENT_VALCHANGE) {
180         char *s = dlg_editbox_get(ctrl, dlg);
181         if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
182             conf_set_str(conf, CONF_serline, s);
183         else
184             conf_set_str(conf, CONF_host, s);
185         sfree(s);
186     }
187 }
188
189 static void config_port_handler(union control *ctrl, void *dlg,
190                                 void *data, int event)
191 {
192     Conf *conf = (Conf *)data;
193     char buf[80];
194
195     /*
196      * This function works similarly to the standard edit box handler,
197      * only it has to choose the control's label and text from two
198      * different places depending on the protocol.
199      */
200     if (event == EVENT_REFRESH) {
201         if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
202             /*
203              * This label text is carefully chosen to contain a p,
204              * since that's the shortcut for the port control.
205              */
206             dlg_label_change(ctrl, dlg, "Speed");
207             sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
208         } else {
209             dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
210             if (conf_get_int(conf, CONF_port) != 0)
211                 sprintf(buf, "%d", conf_get_int(conf, CONF_port));
212             else
213                 /* Display an (invalid) port of 0 as blank */
214                 buf[0] = '\0';
215         }
216         dlg_editbox_set(ctrl, dlg, buf);
217     } else if (event == EVENT_VALCHANGE) {
218         char *s = dlg_editbox_get(ctrl, dlg);
219         int i = atoi(s);
220         sfree(s);
221
222         if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
223             conf_set_int(conf, CONF_serspeed, i);
224         else
225             conf_set_int(conf, CONF_port, i);
226     }
227 }
228
229 struct hostport {
230     union control *host, *port;
231 };
232
233 /*
234  * We export this function so that platform-specific config
235  * routines can use it to conveniently identify the protocol radio
236  * buttons in order to add to them.
237  */
238 void config_protocolbuttons_handler(union control *ctrl, void *dlg,
239                                     void *data, int event)
240 {
241     int button;
242     Conf *conf = (Conf *)data;
243     struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
244
245     /*
246      * This function works just like the standard radio-button
247      * handler, except that it also has to change the setting of
248      * the port box, and refresh both host and port boxes when. We
249      * expect the context parameter to point at a hostport
250      * structure giving the `union control's for both.
251      */
252     if (event == EVENT_REFRESH) {
253         int protocol = conf_get_int(conf, CONF_protocol);
254         for (button = 0; button < ctrl->radio.nbuttons; button++)
255             if (protocol == ctrl->radio.buttondata[button].i)
256                 break;
257         /* We expected that `break' to happen, in all circumstances. */
258         assert(button < ctrl->radio.nbuttons);
259         dlg_radiobutton_set(ctrl, dlg, button);
260     } else if (event == EVENT_VALCHANGE) {
261         int oldproto = conf_get_int(conf, CONF_protocol);
262         int newproto, port;
263
264         button = dlg_radiobutton_get(ctrl, dlg);
265         assert(button >= 0 && button < ctrl->radio.nbuttons);
266         newproto = ctrl->radio.buttondata[button].i;
267         conf_set_int(conf, CONF_protocol, newproto);
268
269         if (oldproto != newproto) {
270             Backend *ob = backend_from_proto(oldproto);
271             Backend *nb = backend_from_proto(newproto);
272             assert(ob);
273             assert(nb);
274             /* Iff the user hasn't changed the port from the old protocol's
275              * default, update it with the new protocol's default.
276              * (This includes a "default" of 0, implying that there is no
277              * sensible default for that protocol; in this case it's
278              * displayed as a blank.)
279              * This helps with the common case of tabbing through the
280              * controls in order and setting a non-default port before
281              * getting to the protocol; we want that non-default port
282              * to be preserved. */
283             port = conf_get_int(conf, CONF_port);
284             if (port == ob->default_port)
285                 conf_set_int(conf, CONF_port, nb->default_port);
286         }
287         dlg_refresh(hp->host, dlg);
288         dlg_refresh(hp->port, dlg);
289     }
290 }
291
292 static void loggingbuttons_handler(union control *ctrl, void *dlg,
293                                    void *data, int event)
294 {
295     int button;
296     Conf *conf = (Conf *)data;
297     /* This function works just like the standard radio-button handler,
298      * but it has to fall back to "no logging" in situations where the
299      * configured logging type isn't applicable.
300      */
301     if (event == EVENT_REFRESH) {
302         int logtype = conf_get_int(conf, CONF_logtype);
303
304         for (button = 0; button < ctrl->radio.nbuttons; button++)
305             if (logtype == ctrl->radio.buttondata[button].i)
306                 break;
307
308         /* We fell off the end, so we lack the configured logging type */
309         if (button == ctrl->radio.nbuttons) {
310             button = 0;
311             conf_set_int(conf, CONF_logtype, LGTYP_NONE);
312         }
313         dlg_radiobutton_set(ctrl, dlg, button);
314     } else if (event == EVENT_VALCHANGE) {
315         button = dlg_radiobutton_get(ctrl, dlg);
316         assert(button >= 0 && button < ctrl->radio.nbuttons);
317         conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
318     }
319 }
320
321 static void numeric_keypad_handler(union control *ctrl, void *dlg,
322                                    void *data, int event)
323 {
324     int button;
325     Conf *conf = (Conf *)data;
326     /*
327      * This function works much like the standard radio button
328      * handler, but it has to handle two fields in Conf.
329      */
330     if (event == EVENT_REFRESH) {
331         if (conf_get_int(conf, CONF_nethack_keypad))
332             button = 2;
333         else if (conf_get_int(conf, CONF_app_keypad))
334             button = 1;
335         else
336             button = 0;
337         assert(button < ctrl->radio.nbuttons);
338         dlg_radiobutton_set(ctrl, dlg, button);
339     } else if (event == EVENT_VALCHANGE) {
340         button = dlg_radiobutton_get(ctrl, dlg);
341         assert(button >= 0 && button < ctrl->radio.nbuttons);
342         if (button == 2) {
343             conf_set_int(conf, CONF_app_keypad, FALSE);
344             conf_set_int(conf, CONF_nethack_keypad, TRUE);
345         } else {
346             conf_set_int(conf, CONF_app_keypad, (button != 0));
347             conf_set_int(conf, CONF_nethack_keypad, FALSE);
348         }
349     }
350 }
351
352 static void cipherlist_handler(union control *ctrl, void *dlg,
353                                void *data, int event)
354 {
355     Conf *conf = (Conf *)data;
356     if (event == EVENT_REFRESH) {
357         int i;
358
359         static const struct { const char *s; int c; } ciphers[] = {
360             { "ChaCha20 (SSH-2 only)",  CIPHER_CHACHA20 },
361             { "3DES",                   CIPHER_3DES },
362             { "Blowfish",               CIPHER_BLOWFISH },
363             { "DES",                    CIPHER_DES },
364             { "AES (SSH-2 only)",       CIPHER_AES },
365             { "Arcfour (SSH-2 only)",   CIPHER_ARCFOUR },
366             { "-- warn below here --",  CIPHER_WARN }
367         };
368
369         /* Set up the "selected ciphers" box. */
370         /* (cipherlist assumed to contain all ciphers) */
371         dlg_update_start(ctrl, dlg);
372         dlg_listbox_clear(ctrl, dlg);
373         for (i = 0; i < CIPHER_MAX; i++) {
374             int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
375             int j;
376             const char *cstr = NULL;
377             for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
378                 if (ciphers[j].c == c) {
379                     cstr = ciphers[j].s;
380                     break;
381                 }
382             }
383             dlg_listbox_addwithid(ctrl, dlg, cstr, c);
384         }
385         dlg_update_done(ctrl, dlg);
386
387     } else if (event == EVENT_VALCHANGE) {
388         int i;
389
390         /* Update array to match the list box. */
391         for (i=0; i < CIPHER_MAX; i++)
392             conf_set_int_int(conf, CONF_ssh_cipherlist, i,
393                              dlg_listbox_getid(ctrl, dlg, i));
394     }
395 }
396
397 #ifndef NO_GSSAPI
398 static void gsslist_handler(union control *ctrl, void *dlg,
399                             void *data, int event)
400 {
401     Conf *conf = (Conf *)data;
402     if (event == EVENT_REFRESH) {
403         int i;
404
405         dlg_update_start(ctrl, dlg);
406         dlg_listbox_clear(ctrl, dlg);
407         for (i = 0; i < ngsslibs; i++) {
408             int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
409             assert(id >= 0 && id < ngsslibs);
410             dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
411         }
412         dlg_update_done(ctrl, dlg);
413
414     } else if (event == EVENT_VALCHANGE) {
415         int i;
416
417         /* Update array to match the list box. */
418         for (i=0; i < ngsslibs; i++)
419             conf_set_int_int(conf, CONF_ssh_gsslist, i,
420                              dlg_listbox_getid(ctrl, dlg, i));
421     }
422 }
423 #endif
424
425 static void kexlist_handler(union control *ctrl, void *dlg,
426                             void *data, int event)
427 {
428     Conf *conf = (Conf *)data;
429     if (event == EVENT_REFRESH) {
430         int i;
431
432         static const struct { const char *s; int k; } kexes[] = {
433             { "Diffie-Hellman group 1",         KEX_DHGROUP1 },
434             { "Diffie-Hellman group 14",        KEX_DHGROUP14 },
435             { "Diffie-Hellman group exchange",  KEX_DHGEX },
436             { "RSA-based key exchange",         KEX_RSA },
437             { "ECDH key exchange",              KEX_ECDH },
438             { "-- warn below here --",          KEX_WARN }
439         };
440
441         /* Set up the "kex preference" box. */
442         /* (kexlist assumed to contain all algorithms) */
443         dlg_update_start(ctrl, dlg);
444         dlg_listbox_clear(ctrl, dlg);
445         for (i = 0; i < KEX_MAX; i++) {
446             int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
447             int j;
448             const char *kstr = NULL;
449             for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
450                 if (kexes[j].k == k) {
451                     kstr = kexes[j].s;
452                     break;
453                 }
454             }
455             dlg_listbox_addwithid(ctrl, dlg, kstr, k);
456         }
457         dlg_update_done(ctrl, dlg);
458
459     } else if (event == EVENT_VALCHANGE) {
460         int i;
461
462         /* Update array to match the list box. */
463         for (i=0; i < KEX_MAX; i++)
464             conf_set_int_int(conf, CONF_ssh_kexlist, i,
465                              dlg_listbox_getid(ctrl, dlg, i));
466     }
467 }
468
469 static void printerbox_handler(union control *ctrl, void *dlg,
470                                void *data, int event)
471 {
472     Conf *conf = (Conf *)data;
473     if (event == EVENT_REFRESH) {
474         int nprinters, i;
475         printer_enum *pe;
476         const char *printer;
477
478         dlg_update_start(ctrl, dlg);
479         /*
480          * Some backends may wish to disable the drop-down list on
481          * this edit box. Be prepared for this.
482          */
483         if (ctrl->editbox.has_list) {
484             dlg_listbox_clear(ctrl, dlg);
485             dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
486             pe = printer_start_enum(&nprinters);
487             for (i = 0; i < nprinters; i++)
488                 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
489             printer_finish_enum(pe);
490         }
491         printer = conf_get_str(conf, CONF_printer);
492         if (!printer)
493             printer = PRINTER_DISABLED_STRING;
494         dlg_editbox_set(ctrl, dlg, printer);
495         dlg_update_done(ctrl, dlg);
496     } else if (event == EVENT_VALCHANGE) {
497         char *printer = dlg_editbox_get(ctrl, dlg);
498         if (!strcmp(printer, PRINTER_DISABLED_STRING))
499             printer[0] = '\0';
500         conf_set_str(conf, CONF_printer, printer);
501         sfree(printer);
502     }
503 }
504
505 static void codepage_handler(union control *ctrl, void *dlg,
506                              void *data, int event)
507 {
508     Conf *conf = (Conf *)data;
509     if (event == EVENT_REFRESH) {
510         int i;
511         const char *cp, *thiscp;
512         dlg_update_start(ctrl, dlg);
513         thiscp = cp_name(decode_codepage(conf_get_str(conf,
514                                                       CONF_line_codepage)));
515         dlg_listbox_clear(ctrl, dlg);
516         for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
517             dlg_listbox_add(ctrl, dlg, cp);
518         dlg_editbox_set(ctrl, dlg, thiscp);
519         conf_set_str(conf, CONF_line_codepage, thiscp);
520         dlg_update_done(ctrl, dlg);
521     } else if (event == EVENT_VALCHANGE) {
522         char *codepage = dlg_editbox_get(ctrl, dlg);
523         conf_set_str(conf, CONF_line_codepage,
524                      cp_name(decode_codepage(codepage)));
525         sfree(codepage);
526     }
527 }
528
529 static void sshbug_handler(union control *ctrl, void *dlg,
530                            void *data, int event)
531 {
532     Conf *conf = (Conf *)data;
533     if (event == EVENT_REFRESH) {
534         /*
535          * We must fetch the previously configured value from the Conf
536          * before we start modifying the drop-down list, otherwise the
537          * spurious SELCHANGE we trigger in the process will overwrite
538          * the value we wanted to keep.
539          */
540         int oldconf = conf_get_int(conf, ctrl->listbox.context.i);
541         dlg_update_start(ctrl, dlg);
542         dlg_listbox_clear(ctrl, dlg);
543         dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
544         dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
545         dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
546         switch (oldconf) {
547           case AUTO:      dlg_listbox_select(ctrl, dlg, 0); break;
548           case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
549           case FORCE_ON:  dlg_listbox_select(ctrl, dlg, 2); break;
550         }
551         dlg_update_done(ctrl, dlg);
552     } else if (event == EVENT_SELCHANGE) {
553         int i = dlg_listbox_index(ctrl, dlg);
554         if (i < 0)
555             i = AUTO;
556         else
557             i = dlg_listbox_getid(ctrl, dlg, i);
558         conf_set_int(conf, ctrl->listbox.context.i, i);
559     }
560 }
561
562 struct sessionsaver_data {
563     union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
564     union control *okbutton, *cancelbutton;
565     struct sesslist sesslist;
566     int midsession;
567     char *savedsession;     /* the current contents of ssd->editbox */
568 };
569
570 static void sessionsaver_data_free(void *ssdv)
571 {
572     struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
573     get_sesslist(&ssd->sesslist, FALSE);
574     sfree(ssd->savedsession);
575     sfree(ssd);
576 }
577
578 /* 
579  * Helper function to load the session selected in the list box, if
580  * any, as this is done in more than one place below. Returns 0 for
581  * failure.
582  */
583 static int load_selected_session(struct sessionsaver_data *ssd,
584                                  void *dlg, Conf *conf, int *maybe_launch)
585 {
586     int i = dlg_listbox_index(ssd->listbox, dlg);
587     int isdef;
588     if (i < 0) {
589         dlg_beep(dlg);
590         return 0;
591     }
592     isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
593     load_settings(ssd->sesslist.sessions[i], conf);
594     sfree(ssd->savedsession);
595     ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]);
596     if (maybe_launch)
597         *maybe_launch = !isdef;
598     dlg_refresh(NULL, dlg);
599     /* Restore the selection, which might have been clobbered by
600      * changing the value of the edit box. */
601     dlg_listbox_select(ssd->listbox, dlg, i);
602     return 1;
603 }
604
605 static void sessionsaver_handler(union control *ctrl, void *dlg,
606                                  void *data, int event)
607 {
608     Conf *conf = (Conf *)data;
609     struct sessionsaver_data *ssd =
610         (struct sessionsaver_data *)ctrl->generic.context.p;
611
612     if (event == EVENT_REFRESH) {
613         if (ctrl == ssd->editbox) {
614             dlg_editbox_set(ctrl, dlg, ssd->savedsession);
615         } else if (ctrl == ssd->listbox) {
616             int i;
617             dlg_update_start(ctrl, dlg);
618             dlg_listbox_clear(ctrl, dlg);
619             for (i = 0; i < ssd->sesslist.nsessions; i++)
620                 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
621             dlg_update_done(ctrl, dlg);
622         }
623     } else if (event == EVENT_VALCHANGE) {
624         int top, bottom, halfway, i;
625         if (ctrl == ssd->editbox) {
626             sfree(ssd->savedsession);
627             ssd->savedsession = dlg_editbox_get(ctrl, dlg);
628             top = ssd->sesslist.nsessions;
629             bottom = -1;
630             while (top-bottom > 1) {
631                 halfway = (top+bottom)/2;
632                 i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]);
633                 if (i <= 0 ) {
634                     top = halfway;
635                 } else {
636                     bottom = halfway;
637                 }
638             }
639             if (top == ssd->sesslist.nsessions) {
640                 top -= 1;
641             }
642             dlg_listbox_select(ssd->listbox, dlg, top);
643         }
644     } else if (event == EVENT_ACTION) {
645         int mbl = FALSE;
646         if (!ssd->midsession &&
647             (ctrl == ssd->listbox ||
648              (ssd->loadbutton && ctrl == ssd->loadbutton))) {
649             /*
650              * The user has double-clicked a session, or hit Load.
651              * We must load the selected session, and then
652              * terminate the configuration dialog _if_ there was a
653              * double-click on the list box _and_ that session
654              * contains a hostname.
655              */
656             if (load_selected_session(ssd, dlg, conf, &mbl) &&
657                 (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
658                 dlg_end(dlg, 1);       /* it's all over, and succeeded */
659             }
660         } else if (ctrl == ssd->savebutton) {
661             int isdef = !strcmp(ssd->savedsession, "Default Settings");
662             if (!ssd->savedsession[0]) {
663                 int i = dlg_listbox_index(ssd->listbox, dlg);
664                 if (i < 0) {
665                     dlg_beep(dlg);
666                     return;
667                 }
668                 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
669                 sfree(ssd->savedsession);
670                 ssd->savedsession = dupstr(isdef ? "" :
671                                            ssd->sesslist.sessions[i]);
672             }
673             {
674                 char *errmsg = save_settings(ssd->savedsession, conf);
675                 if (errmsg) {
676                     dlg_error_msg(dlg, errmsg);
677                     sfree(errmsg);
678                 }
679             }
680             get_sesslist(&ssd->sesslist, FALSE);
681             get_sesslist(&ssd->sesslist, TRUE);
682             dlg_refresh(ssd->editbox, dlg);
683             dlg_refresh(ssd->listbox, dlg);
684         } else if (!ssd->midsession &&
685                    ssd->delbutton && ctrl == ssd->delbutton) {
686             int i = dlg_listbox_index(ssd->listbox, dlg);
687             if (i <= 0) {
688                 dlg_beep(dlg);
689             } else {
690                 del_settings(ssd->sesslist.sessions[i]);
691                 get_sesslist(&ssd->sesslist, FALSE);
692                 get_sesslist(&ssd->sesslist, TRUE);
693                 dlg_refresh(ssd->listbox, dlg);
694             }
695         } else if (ctrl == ssd->okbutton) {
696             if (ssd->midsession) {
697                 /* In a mid-session Change Settings, Apply is always OK. */
698                 dlg_end(dlg, 1);
699                 return;
700             }
701             /*
702              * Annoying special case. If the `Open' button is
703              * pressed while no host name is currently set, _and_
704              * the session list previously had the focus, _and_
705              * there was a session selected in that which had a
706              * valid host name in it, then load it and go.
707              */
708             if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
709                 !conf_launchable(conf)) {
710                 Conf *conf2 = conf_new();
711                 int mbl = FALSE;
712                 if (!load_selected_session(ssd, dlg, conf2, &mbl)) {
713                     dlg_beep(dlg);
714                     conf_free(conf2);
715                     return;
716                 }
717                 /* If at this point we have a valid session, go! */
718                 if (mbl && conf_launchable(conf2)) {
719                     conf_copy_into(conf, conf2);
720                     dlg_end(dlg, 1);
721                 } else
722                     dlg_beep(dlg);
723
724                 conf_free(conf2);
725                 return;
726             }
727
728             /*
729              * Otherwise, do the normal thing: if we have a valid
730              * session, get going.
731              */
732             if (conf_launchable(conf)) {
733                 dlg_end(dlg, 1);
734             } else
735                 dlg_beep(dlg);
736         } else if (ctrl == ssd->cancelbutton) {
737             dlg_end(dlg, 0);
738         }
739     }
740 }
741
742 struct charclass_data {
743     union control *listbox, *editbox, *button;
744 };
745
746 static void charclass_handler(union control *ctrl, void *dlg,
747                               void *data, int event)
748 {
749     Conf *conf = (Conf *)data;
750     struct charclass_data *ccd =
751         (struct charclass_data *)ctrl->generic.context.p;
752
753     if (event == EVENT_REFRESH) {
754         if (ctrl == ccd->listbox) {
755             int i;
756             dlg_update_start(ctrl, dlg);
757             dlg_listbox_clear(ctrl, dlg);
758             for (i = 0; i < 128; i++) {
759                 char str[100];
760                 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
761                         (i >= 0x21 && i != 0x7F) ? i : ' ',
762                         conf_get_int_int(conf, CONF_wordness, i));
763                 dlg_listbox_add(ctrl, dlg, str);
764             }
765             dlg_update_done(ctrl, dlg);
766         }
767     } else if (event == EVENT_ACTION) {
768         if (ctrl == ccd->button) {
769             char *str;
770             int i, n;
771             str = dlg_editbox_get(ccd->editbox, dlg);
772             n = atoi(str);
773             sfree(str);
774             for (i = 0; i < 128; i++) {
775                 if (dlg_listbox_issel(ccd->listbox, dlg, i))
776                     conf_set_int_int(conf, CONF_wordness, i, n);
777             }
778             dlg_refresh(ccd->listbox, dlg);
779         }
780     }
781 }
782
783 struct colour_data {
784     union control *listbox, *redit, *gedit, *bedit, *button;
785 };
786
787 static const char *const colours[] = {
788     "Default Foreground", "Default Bold Foreground",
789     "Default Background", "Default Bold Background",
790     "Cursor Text", "Cursor Colour",
791     "ANSI Black", "ANSI Black Bold",
792     "ANSI Red", "ANSI Red Bold",
793     "ANSI Green", "ANSI Green Bold",
794     "ANSI Yellow", "ANSI Yellow Bold",
795     "ANSI Blue", "ANSI Blue Bold",
796     "ANSI Magenta", "ANSI Magenta Bold",
797     "ANSI Cyan", "ANSI Cyan Bold",
798     "ANSI White", "ANSI White Bold"
799 };
800
801 static void colour_handler(union control *ctrl, void *dlg,
802                             void *data, int event)
803 {
804     Conf *conf = (Conf *)data;
805     struct colour_data *cd =
806         (struct colour_data *)ctrl->generic.context.p;
807     int update = FALSE, clear = FALSE, r, g, b;
808
809     if (event == EVENT_REFRESH) {
810         if (ctrl == cd->listbox) {
811             int i;
812             dlg_update_start(ctrl, dlg);
813             dlg_listbox_clear(ctrl, dlg);
814             for (i = 0; i < lenof(colours); i++)
815                 dlg_listbox_add(ctrl, dlg, colours[i]);
816             dlg_update_done(ctrl, dlg);
817             clear = TRUE;
818             update = TRUE;
819         }
820     } else if (event == EVENT_SELCHANGE) {
821         if (ctrl == cd->listbox) {
822             /* The user has selected a colour. Update the RGB text. */
823             int i = dlg_listbox_index(ctrl, dlg);
824             if (i < 0) {
825                 clear = TRUE;
826             } else {
827                 clear = FALSE;
828                 r = conf_get_int_int(conf, CONF_colours, i*3+0);
829                 g = conf_get_int_int(conf, CONF_colours, i*3+1);
830                 b = conf_get_int_int(conf, CONF_colours, i*3+2);
831             }
832             update = TRUE;
833         }
834     } else if (event == EVENT_VALCHANGE) {
835         if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
836             /* The user has changed the colour using the edit boxes. */
837             char *str;
838             int i, cval;
839
840             str = dlg_editbox_get(ctrl, dlg);
841             cval = atoi(str);
842             sfree(str);
843             if (cval > 255) cval = 255;
844             if (cval < 0)   cval = 0;
845
846             i = dlg_listbox_index(cd->listbox, dlg);
847             if (i >= 0) {
848                 if (ctrl == cd->redit)
849                     conf_set_int_int(conf, CONF_colours, i*3+0, cval);
850                 else if (ctrl == cd->gedit)
851                     conf_set_int_int(conf, CONF_colours, i*3+1, cval);
852                 else if (ctrl == cd->bedit)
853                     conf_set_int_int(conf, CONF_colours, i*3+2, cval);
854             }
855         }
856     } else if (event == EVENT_ACTION) {
857         if (ctrl == cd->button) {
858             int i = dlg_listbox_index(cd->listbox, dlg);
859             if (i < 0) {
860                 dlg_beep(dlg);
861                 return;
862             }
863             /*
864              * Start a colour selector, which will send us an
865              * EVENT_CALLBACK when it's finished and allow us to
866              * pick up the results.
867              */
868             dlg_coloursel_start(ctrl, dlg,
869                                 conf_get_int_int(conf, CONF_colours, i*3+0),
870                                 conf_get_int_int(conf, CONF_colours, i*3+1),
871                                 conf_get_int_int(conf, CONF_colours, i*3+2));
872         }
873     } else if (event == EVENT_CALLBACK) {
874         if (ctrl == cd->button) {
875             int i = dlg_listbox_index(cd->listbox, dlg);
876             /*
877              * Collect the results of the colour selector. Will
878              * return nonzero on success, or zero if the colour
879              * selector did nothing (user hit Cancel, for example).
880              */
881             if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
882                 conf_set_int_int(conf, CONF_colours, i*3+0, r);
883                 conf_set_int_int(conf, CONF_colours, i*3+1, g);
884                 conf_set_int_int(conf, CONF_colours, i*3+2, b);
885                 clear = FALSE;
886                 update = TRUE;
887             }
888         }
889     }
890
891     if (update) {
892         if (clear) {
893             dlg_editbox_set(cd->redit, dlg, "");
894             dlg_editbox_set(cd->gedit, dlg, "");
895             dlg_editbox_set(cd->bedit, dlg, "");
896         } else {
897             char buf[40];
898             sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
899             sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
900             sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
901         }
902     }
903 }
904
905 struct ttymodes_data {
906     union control *modelist, *valradio, *valbox;
907     union control *addbutton, *rembutton, *listbox;
908 };
909
910 static void ttymodes_handler(union control *ctrl, void *dlg,
911                              void *data, int event)
912 {
913     Conf *conf = (Conf *)data;
914     struct ttymodes_data *td =
915         (struct ttymodes_data *)ctrl->generic.context.p;
916
917     if (event == EVENT_REFRESH) {
918         if (ctrl == td->listbox) {
919             char *key, *val;
920             dlg_update_start(ctrl, dlg);
921             dlg_listbox_clear(ctrl, dlg);
922             for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
923                  val != NULL;
924                  val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
925                 char *disp = dupprintf("%s\t%s", key,
926                                        (val[0] == 'A') ? "(auto)" : val+1);
927                 dlg_listbox_add(ctrl, dlg, disp);
928                 sfree(disp);
929             }
930             dlg_update_done(ctrl, dlg);
931         } else if (ctrl == td->modelist) {
932             int i;
933             dlg_update_start(ctrl, dlg);
934             dlg_listbox_clear(ctrl, dlg);
935             for (i = 0; ttymodes[i]; i++)
936                 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
937             dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
938             dlg_update_done(ctrl, dlg);
939         } else if (ctrl == td->valradio) {
940             dlg_radiobutton_set(ctrl, dlg, 0);
941         }
942     } else if (event == EVENT_ACTION) {
943         if (ctrl == td->addbutton) {
944             int ind = dlg_listbox_index(td->modelist, dlg);
945             if (ind >= 0) {
946                 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
947                 const char *key;
948                 char *str, *val;
949                 /* Construct new entry */
950                 key = ttymodes[ind];
951                 str = dlg_editbox_get(td->valbox, dlg);
952                 val = dupprintf("%c%s", type, str);
953                 sfree(str);
954                 conf_set_str_str(conf, CONF_ttymodes, key, val);
955                 sfree(val);
956                 dlg_refresh(td->listbox, dlg);
957             } else
958                 dlg_beep(dlg);
959         } else if (ctrl == td->rembutton) {
960             int i = 0;
961             char *key, *val;
962             int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
963             for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
964                  val != NULL;
965                  val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
966                 if (dlg_listbox_issel(td->listbox, dlg, i)) {
967                     if (!multisel) {
968                         /* Populate controls with entry we're about to
969                          * delete, for ease of editing.
970                          * (If multiple entries were selected, don't
971                          * touch the controls.) */
972                         int ind = 0;
973                         val++;
974                         while (ttymodes[ind]) {
975                             if (!strcmp(ttymodes[ind], key))
976                                 break;
977                             ind++;
978                         }
979                         dlg_listbox_select(td->modelist, dlg, ind);
980                         dlg_radiobutton_set(td->valradio, dlg,
981                                             (*val == 'V'));
982                         dlg_editbox_set(td->valbox, dlg, val+1);
983                     }
984                     conf_del_str_str(conf, CONF_ttymodes, key);
985                 }
986                 i++;
987             }
988             dlg_refresh(td->listbox, dlg);
989         }
990     }
991 }
992
993 struct environ_data {
994     union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
995 };
996
997 static void environ_handler(union control *ctrl, void *dlg,
998                             void *data, int event)
999 {
1000     Conf *conf = (Conf *)data;
1001     struct environ_data *ed =
1002         (struct environ_data *)ctrl->generic.context.p;
1003
1004     if (event == EVENT_REFRESH) {
1005         if (ctrl == ed->listbox) {
1006             char *key, *val;
1007             dlg_update_start(ctrl, dlg);
1008             dlg_listbox_clear(ctrl, dlg);
1009             for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1010                  val != NULL;
1011                  val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1012                 char *p = dupprintf("%s\t%s", key, val);
1013                 dlg_listbox_add(ctrl, dlg, p);
1014                 sfree(p);
1015             }
1016             dlg_update_done(ctrl, dlg);
1017         }
1018     } else if (event == EVENT_ACTION) {
1019         if (ctrl == ed->addbutton) {
1020             char *key, *val, *str;
1021             key = dlg_editbox_get(ed->varbox, dlg);
1022             if (!*key) {
1023                 sfree(key);
1024                 dlg_beep(dlg);
1025                 return;
1026             }
1027             val = dlg_editbox_get(ed->valbox, dlg);
1028             if (!*val) {
1029                 sfree(key);
1030                 sfree(val);
1031                 dlg_beep(dlg);
1032                 return;
1033             }
1034             conf_set_str_str(conf, CONF_environmt, key, val);
1035             str = dupcat(key, "\t", val, NULL);
1036             dlg_editbox_set(ed->varbox, dlg, "");
1037             dlg_editbox_set(ed->valbox, dlg, "");
1038             sfree(str);
1039             sfree(key);
1040             sfree(val);
1041             dlg_refresh(ed->listbox, dlg);
1042         } else if (ctrl == ed->rembutton) {
1043             int i = dlg_listbox_index(ed->listbox, dlg);
1044             if (i < 0) {
1045                 dlg_beep(dlg);
1046             } else {
1047                 char *key, *val;
1048
1049                 key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
1050                 if (key) {
1051                     /* Populate controls with the entry we're about to delete
1052                      * for ease of editing */
1053                     val = conf_get_str_str(conf, CONF_environmt, key);
1054                     dlg_editbox_set(ed->varbox, dlg, key);
1055                     dlg_editbox_set(ed->valbox, dlg, val);
1056                     /* And delete it */
1057                     conf_del_str_str(conf, CONF_environmt, key);
1058                 }
1059             }
1060             dlg_refresh(ed->listbox, dlg);
1061         }
1062     }
1063 }
1064
1065 struct portfwd_data {
1066     union control *addbutton, *rembutton, *listbox;
1067     union control *sourcebox, *destbox, *direction;
1068 #ifndef NO_IPV6
1069     union control *addressfamily;
1070 #endif
1071 };
1072
1073 static void portfwd_handler(union control *ctrl, void *dlg,
1074                             void *data, int event)
1075 {
1076     Conf *conf = (Conf *)data;
1077     struct portfwd_data *pfd =
1078         (struct portfwd_data *)ctrl->generic.context.p;
1079
1080     if (event == EVENT_REFRESH) {
1081         if (ctrl == pfd->listbox) {
1082             char *key, *val;
1083             dlg_update_start(ctrl, dlg);
1084             dlg_listbox_clear(ctrl, dlg);
1085             for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
1086                  val != NULL;
1087                  val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
1088                 char *p;
1089                 if (!strcmp(val, "D")) {
1090                     char *L;
1091                     /*
1092                      * A dynamic forwarding is stored as L12345=D or
1093                      * 6L12345=D (since it's mutually exclusive with
1094                      * L12345=anything else), but displayed as D12345
1095                      * to match the fiction that 'Local', 'Remote' and
1096                      * 'Dynamic' are three distinct modes and also to
1097                      * align with OpenSSH's command line option syntax
1098                      * that people will already be used to. So, for
1099                      * display purposes, find the L in the key string
1100                      * and turn it into a D.
1101                      */
1102                     p = dupprintf("%s\t", key);
1103                     L = strchr(p, 'L');
1104                     if (L) *L = 'D';
1105                 } else
1106                     p = dupprintf("%s\t%s", key, val);
1107                 dlg_listbox_add(ctrl, dlg, p);
1108                 sfree(p);
1109             }
1110             dlg_update_done(ctrl, dlg);
1111         } else if (ctrl == pfd->direction) {
1112             /*
1113              * Default is Local.
1114              */
1115             dlg_radiobutton_set(ctrl, dlg, 0);
1116 #ifndef NO_IPV6
1117         } else if (ctrl == pfd->addressfamily) {
1118             dlg_radiobutton_set(ctrl, dlg, 0);
1119 #endif
1120         }
1121     } else if (event == EVENT_ACTION) {
1122         if (ctrl == pfd->addbutton) {
1123             const char *family, *type;
1124             char *src, *key, *val;
1125             int whichbutton;
1126
1127 #ifndef NO_IPV6
1128             whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1129             if (whichbutton == 1)
1130                 family = "4";
1131             else if (whichbutton == 2)
1132                 family = "6";
1133             else
1134 #endif
1135                 family = "";
1136
1137             whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1138             if (whichbutton == 0)
1139                 type = "L";
1140             else if (whichbutton == 1)
1141                 type = "R";
1142             else
1143                 type = "D";
1144
1145             src = dlg_editbox_get(pfd->sourcebox, dlg);
1146             if (!*src) {
1147                 dlg_error_msg(dlg, "You need to specify a source port number");
1148                 sfree(src);
1149                 return;
1150             }
1151             if (*type != 'D') {
1152                 val = dlg_editbox_get(pfd->destbox, dlg);
1153                 if (!*val || !host_strchr(val, ':')) {
1154                     dlg_error_msg(dlg,
1155                                   "You need to specify a destination address\n"
1156                                   "in the form \"host.name:port\"");
1157                     sfree(src);
1158                     sfree(val);
1159                     return;
1160                 }
1161             } else {
1162                 type = "L";
1163                 val = dupstr("D");     /* special case */
1164             }
1165
1166             key = dupcat(family, type, src, NULL);
1167             sfree(src);
1168
1169             if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1170                 dlg_error_msg(dlg, "Specified forwarding already exists");
1171             } else {
1172                 conf_set_str_str(conf, CONF_portfwd, key, val);
1173             }
1174
1175             sfree(key);
1176             sfree(val);
1177             dlg_refresh(pfd->listbox, dlg);
1178         } else if (ctrl == pfd->rembutton) {
1179             int i = dlg_listbox_index(pfd->listbox, dlg);
1180             if (i < 0) {
1181                 dlg_beep(dlg);
1182             } else {
1183                 char *key, *p;
1184                 const char *val;
1185
1186                 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1187                 if (key) {
1188                     static const char *const afs = "A46";
1189                     static const char *const dirs = "LRD";
1190                     char *afp;
1191                     int dir;
1192 #ifndef NO_IPV6
1193                     int idx;
1194 #endif
1195
1196                     /* Populate controls with the entry we're about to delete
1197                      * for ease of editing */
1198                     p = key;
1199
1200                     afp = strchr(afs, *p);
1201 #ifndef NO_IPV6
1202                     idx = afp ? afp-afs : 0;
1203 #endif
1204                     if (afp)
1205                         p++;
1206 #ifndef NO_IPV6
1207                     dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1208 #endif
1209
1210                     dir = *p;
1211
1212                     val = conf_get_str_str(conf, CONF_portfwd, key);
1213                     if (!strcmp(val, "D")) {
1214                         dir = 'D';
1215                         val = "";
1216                     }
1217
1218                     dlg_radiobutton_set(pfd->direction, dlg,
1219                                         strchr(dirs, dir) - dirs);
1220                     p++;
1221
1222                     dlg_editbox_set(pfd->sourcebox, dlg, p);
1223                     dlg_editbox_set(pfd->destbox, dlg, val);
1224                     /* And delete it */
1225                     conf_del_str_str(conf, CONF_portfwd, key);
1226                 }
1227             }
1228             dlg_refresh(pfd->listbox, dlg);
1229         }
1230     }
1231 }
1232
1233 struct manual_hostkey_data {
1234     union control *addbutton, *rembutton, *listbox, *keybox;
1235 };
1236
1237 static void manual_hostkey_handler(union control *ctrl, void *dlg,
1238                                    void *data, int event)
1239 {
1240     Conf *conf = (Conf *)data;
1241     struct manual_hostkey_data *mh =
1242         (struct manual_hostkey_data *)ctrl->generic.context.p;
1243
1244     if (event == EVENT_REFRESH) {
1245         if (ctrl == mh->listbox) {
1246             char *key, *val;
1247             dlg_update_start(ctrl, dlg);
1248             dlg_listbox_clear(ctrl, dlg);
1249             for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1250                                          NULL, &key);
1251                  val != NULL;
1252                  val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1253                                          key, &key)) {
1254                 dlg_listbox_add(ctrl, dlg, key);
1255             }
1256             dlg_update_done(ctrl, dlg);
1257         }
1258     } else if (event == EVENT_ACTION) {
1259         if (ctrl == mh->addbutton) {
1260             char *key;
1261
1262             key = dlg_editbox_get(mh->keybox, dlg);
1263             if (!*key) {
1264                 dlg_error_msg(dlg, "You need to specify a host key or "
1265                               "fingerprint");
1266                 sfree(key);
1267                 return;
1268             }
1269
1270             if (!validate_manual_hostkey(key)) {
1271                 dlg_error_msg(dlg, "Host key is not in a valid format");
1272             } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
1273                                             key)) {
1274                 dlg_error_msg(dlg, "Specified host key is already listed");
1275             } else {
1276                 conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
1277             }
1278
1279             sfree(key);
1280             dlg_refresh(mh->listbox, dlg);
1281         } else if (ctrl == mh->rembutton) {
1282             int i = dlg_listbox_index(mh->listbox, dlg);
1283             if (i < 0) {
1284                 dlg_beep(dlg);
1285             } else {
1286                 char *key;
1287
1288                 key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
1289                 if (key) {
1290                     dlg_editbox_set(mh->keybox, dlg, key);
1291                     /* And delete it */
1292                     conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
1293                 }
1294             }
1295             dlg_refresh(mh->listbox, dlg);
1296         }
1297     }
1298 }
1299
1300 void setup_config_box(struct controlbox *b, int midsession,
1301                       int protocol, int protcfginfo)
1302 {
1303     struct controlset *s;
1304     struct sessionsaver_data *ssd;
1305     struct charclass_data *ccd;
1306     struct colour_data *cd;
1307     struct ttymodes_data *td;
1308     struct environ_data *ed;
1309     struct portfwd_data *pfd;
1310     struct manual_hostkey_data *mh;
1311     union control *c;
1312     char *str;
1313
1314     ssd = (struct sessionsaver_data *)
1315         ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data),
1316                              sessionsaver_data_free);
1317     memset(ssd, 0, sizeof(*ssd));
1318     ssd->savedsession = dupstr("");
1319     ssd->midsession = midsession;
1320
1321     /*
1322      * The standard panel that appears at the bottom of all panels:
1323      * Open, Cancel, Apply etc.
1324      */
1325     s = ctrl_getset(b, "", "", "");
1326     ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1327     ssd->okbutton = ctrl_pushbutton(s,
1328                                     (midsession ? "Apply" : "Open"),
1329                                     (char)(midsession ? 'a' : 'o'),
1330                                     HELPCTX(no_help),
1331                                     sessionsaver_handler, P(ssd));
1332     ssd->okbutton->button.isdefault = TRUE;
1333     ssd->okbutton->generic.column = 3;
1334     ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1335                                         sessionsaver_handler, P(ssd));
1336     ssd->cancelbutton->button.iscancel = TRUE;
1337     ssd->cancelbutton->generic.column = 4;
1338     /* We carefully don't close the 5-column part, so that platform-
1339      * specific add-ons can put extra buttons alongside Open and Cancel. */
1340
1341     /*
1342      * The Session panel.
1343      */
1344     str = dupprintf("Basic options for your %s session", appname);
1345     ctrl_settitle(b, "Session", str);
1346     sfree(str);
1347
1348     if (!midsession) {
1349         struct hostport *hp = (struct hostport *)
1350             ctrl_alloc(b, sizeof(struct hostport));
1351
1352         s = ctrl_getset(b, "Session", "hostport",
1353                         "Specify the destination you want to connect to");
1354         ctrl_columns(s, 2, 75, 25);
1355         c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1356                          HELPCTX(session_hostname),
1357                          config_host_handler, I(0), I(0));
1358         c->generic.column = 0;
1359         hp->host = c;
1360         c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1361                          HELPCTX(session_hostname),
1362                          config_port_handler, I(0), I(0));
1363         c->generic.column = 1;
1364         hp->port = c;
1365         ctrl_columns(s, 1, 100);
1366
1367         if (!backend_from_proto(PROT_SSH)) {
1368             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1369                               HELPCTX(session_hostname),
1370                               config_protocolbuttons_handler, P(hp),
1371                               "Raw", 'w', I(PROT_RAW),
1372                               "Telnet", 't', I(PROT_TELNET),
1373                               "Rlogin", 'i', I(PROT_RLOGIN),
1374                               NULL);
1375         } else {
1376             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1377                               HELPCTX(session_hostname),
1378                               config_protocolbuttons_handler, P(hp),
1379                               "Raw", 'w', I(PROT_RAW),
1380                               "Telnet", 't', I(PROT_TELNET),
1381                               "Rlogin", 'i', I(PROT_RLOGIN),
1382                               "SSH", 's', I(PROT_SSH),
1383                               NULL);
1384         }
1385     }
1386
1387     /*
1388      * The Load/Save panel is available even in mid-session.
1389      */
1390     s = ctrl_getset(b, "Session", "savedsessions",
1391                     midsession ? "Save the current session settings" :
1392                     "Load, save or delete a stored session");
1393     ctrl_columns(s, 2, 75, 25);
1394     get_sesslist(&ssd->sesslist, TRUE);
1395     ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1396                                 HELPCTX(session_saved),
1397                                 sessionsaver_handler, P(ssd), P(NULL));
1398     ssd->editbox->generic.column = 0;
1399     /* Reset columns so that the buttons are alongside the list, rather
1400      * than alongside that edit box. */
1401     ctrl_columns(s, 1, 100);
1402     ctrl_columns(s, 2, 75, 25);
1403     ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1404                                 HELPCTX(session_saved),
1405                                 sessionsaver_handler, P(ssd));
1406     ssd->listbox->generic.column = 0;
1407     ssd->listbox->listbox.height = 7;
1408     if (!midsession) {
1409         ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1410                                           HELPCTX(session_saved),
1411                                           sessionsaver_handler, P(ssd));
1412         ssd->loadbutton->generic.column = 1;
1413     } else {
1414         /* We can't offer the Load button mid-session, as it would allow the
1415          * user to load and subsequently save settings they can't see. (And
1416          * also change otherwise immutable settings underfoot; that probably
1417          * shouldn't be a problem, but.) */
1418         ssd->loadbutton = NULL;
1419     }
1420     /* "Save" button is permitted mid-session. */
1421     ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1422                                       HELPCTX(session_saved),
1423                                       sessionsaver_handler, P(ssd));
1424     ssd->savebutton->generic.column = 1;
1425     if (!midsession) {
1426         ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1427                                          HELPCTX(session_saved),
1428                                          sessionsaver_handler, P(ssd));
1429         ssd->delbutton->generic.column = 1;
1430     } else {
1431         /* Disable the Delete button mid-session too, for UI consistency. */
1432         ssd->delbutton = NULL;
1433     }
1434     ctrl_columns(s, 1, 100);
1435
1436     s = ctrl_getset(b, "Session", "otheropts", NULL);
1437     ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1438                       HELPCTX(session_coe),
1439                       conf_radiobutton_handler,
1440                       I(CONF_close_on_exit),
1441                       "Always", I(FORCE_ON),
1442                       "Never", I(FORCE_OFF),
1443                       "Only on clean exit", I(AUTO), NULL);
1444
1445     /*
1446      * The Session/Logging panel.
1447      */
1448     ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1449
1450     s = ctrl_getset(b, "Session/Logging", "main", NULL);
1451     /*
1452      * The logging buttons change depending on whether SSH packet
1453      * logging can sensibly be available.
1454      */
1455     {
1456         const char *sshlogname, *sshrawlogname;
1457         if ((midsession && protocol == PROT_SSH) ||
1458             (!midsession && backend_from_proto(PROT_SSH))) {
1459             sshlogname = "SSH packets";
1460             sshrawlogname = "SSH packets and raw data";
1461         } else {
1462             sshlogname = NULL;         /* this will disable both buttons */
1463             sshrawlogname = NULL;      /* this will just placate optimisers */
1464         }
1465         ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1466                           HELPCTX(logging_main),
1467                           loggingbuttons_handler,
1468                           I(CONF_logtype),
1469                           "None", 't', I(LGTYP_NONE),
1470                           "Printable output", 'p', I(LGTYP_ASCII),
1471                           "All session output", 'l', I(LGTYP_DEBUG),
1472                           sshlogname, 's', I(LGTYP_PACKETS),
1473                           sshrawlogname, 'r', I(LGTYP_SSHRAW),
1474                           NULL);
1475     }
1476     ctrl_filesel(s, "Log file name:", 'f',
1477                  NULL, TRUE, "Select session log file name",
1478                  HELPCTX(logging_filename),
1479                  conf_filesel_handler, I(CONF_logfilename));
1480     ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1481               " &T for time, &H for host name, and &P for port number)",
1482               HELPCTX(logging_filename));
1483     ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1484                       HELPCTX(logging_exists),
1485                       conf_radiobutton_handler, I(CONF_logxfovr),
1486                       "Always overwrite it", I(LGXF_OVR),
1487                       "Always append to the end of it", I(LGXF_APN),
1488                       "Ask the user every time", I(LGXF_ASK), NULL);
1489     ctrl_checkbox(s, "Flush log file frequently", 'u',
1490                  HELPCTX(logging_flush),
1491                  conf_checkbox_handler, I(CONF_logflush));
1492
1493     if ((midsession && protocol == PROT_SSH) ||
1494         (!midsession && backend_from_proto(PROT_SSH))) {
1495         s = ctrl_getset(b, "Session/Logging", "ssh",
1496                         "Options specific to SSH packet logging");
1497         ctrl_checkbox(s, "Omit known password fields", 'k',
1498                       HELPCTX(logging_ssh_omit_password),
1499                       conf_checkbox_handler, I(CONF_logomitpass));
1500         ctrl_checkbox(s, "Omit session data", 'd',
1501                       HELPCTX(logging_ssh_omit_data),
1502                       conf_checkbox_handler, I(CONF_logomitdata));
1503     }
1504
1505     /*
1506      * The Terminal panel.
1507      */
1508     ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1509
1510     s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1511     ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1512                   HELPCTX(terminal_autowrap),
1513                   conf_checkbox_handler, I(CONF_wrap_mode));
1514     ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1515                   HELPCTX(terminal_decom),
1516                   conf_checkbox_handler, I(CONF_dec_om));
1517     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1518                   HELPCTX(terminal_lfhascr),
1519                   conf_checkbox_handler, I(CONF_lfhascr));
1520     ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1521                   HELPCTX(terminal_crhaslf),
1522                   conf_checkbox_handler, I(CONF_crhaslf));
1523     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1524                   HELPCTX(terminal_bce),
1525                   conf_checkbox_handler, I(CONF_bce));
1526     ctrl_checkbox(s, "Enable blinking text", 'n',
1527                   HELPCTX(terminal_blink),
1528                   conf_checkbox_handler, I(CONF_blinktext));
1529     ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1530                  HELPCTX(terminal_answerback),
1531                  conf_editbox_handler, I(CONF_answerback), I(1));
1532
1533     s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1534     ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1535                       HELPCTX(terminal_localecho),
1536                       conf_radiobutton_handler,I(CONF_localecho),
1537                       "Auto", I(AUTO),
1538                       "Force on", I(FORCE_ON),
1539                       "Force off", I(FORCE_OFF), NULL);
1540     ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1541                       HELPCTX(terminal_localedit),
1542                       conf_radiobutton_handler,I(CONF_localedit),
1543                       "Auto", I(AUTO),
1544                       "Force on", I(FORCE_ON),
1545                       "Force off", I(FORCE_OFF), NULL);
1546
1547     s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1548     ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1549                   HELPCTX(terminal_printing),
1550                   printerbox_handler, P(NULL), P(NULL));
1551
1552     /*
1553      * The Terminal/Keyboard panel.
1554      */
1555     ctrl_settitle(b, "Terminal/Keyboard",
1556                   "Options controlling the effects of keys");
1557
1558     s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1559                     "Change the sequences sent by:");
1560     ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1561                       HELPCTX(keyboard_backspace),
1562                       conf_radiobutton_handler,
1563                       I(CONF_bksp_is_delete),
1564                       "Control-H", I(0), "Control-? (127)", I(1), NULL);
1565     ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1566                       HELPCTX(keyboard_homeend),
1567                       conf_radiobutton_handler,
1568                       I(CONF_rxvt_homeend),
1569                       "Standard", I(0), "rxvt", I(1), NULL);
1570     ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1571                       HELPCTX(keyboard_funkeys),
1572                       conf_radiobutton_handler,
1573                       I(CONF_funky_type),
1574                       "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1575                       "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1576
1577     s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1578                     "Application keypad settings:");
1579     ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1580                       HELPCTX(keyboard_appcursor),
1581                       conf_radiobutton_handler,
1582                       I(CONF_app_cursor),
1583                       "Normal", I(0), "Application", I(1), NULL);
1584     ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1585                       HELPCTX(keyboard_appkeypad),
1586                       numeric_keypad_handler, P(NULL),
1587                       "Normal", I(0), "Application", I(1), "NetHack", I(2),
1588                       NULL);
1589
1590     /*
1591      * The Terminal/Bell panel.
1592      */
1593     ctrl_settitle(b, "Terminal/Bell",
1594                   "Options controlling the terminal bell");
1595
1596     s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1597     ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1598                       HELPCTX(bell_style),
1599                       conf_radiobutton_handler, I(CONF_beep),
1600                       "None (bell disabled)", I(BELL_DISABLED),
1601                       "Make default system alert sound", I(BELL_DEFAULT),
1602                       "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1603
1604     s = ctrl_getset(b, "Terminal/Bell", "overload",
1605                     "Control the bell overload behaviour");
1606     ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1607                   HELPCTX(bell_overload),
1608                   conf_checkbox_handler, I(CONF_bellovl));
1609     ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1610                  HELPCTX(bell_overload),
1611                  conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1612     ctrl_editbox(s, "... in this many seconds", 't', 20,
1613                  HELPCTX(bell_overload),
1614                  conf_editbox_handler, I(CONF_bellovl_t),
1615                  I(-TICKSPERSEC));
1616     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1617               HELPCTX(bell_overload));
1618     ctrl_editbox(s, "Seconds of silence required", 's', 20,
1619                  HELPCTX(bell_overload),
1620                  conf_editbox_handler, I(CONF_bellovl_s),
1621                  I(-TICKSPERSEC));
1622
1623     /*
1624      * The Terminal/Features panel.
1625      */
1626     ctrl_settitle(b, "Terminal/Features",
1627                   "Enabling and disabling advanced terminal features");
1628
1629     s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1630     ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1631                   HELPCTX(features_application),
1632                   conf_checkbox_handler, I(CONF_no_applic_c));
1633     ctrl_checkbox(s, "Disable application keypad mode", 'k',
1634                   HELPCTX(features_application),
1635                   conf_checkbox_handler, I(CONF_no_applic_k));
1636     ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1637                   HELPCTX(features_mouse),
1638                   conf_checkbox_handler, I(CONF_no_mouse_rep));
1639     ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1640                   HELPCTX(features_resize),
1641                   conf_checkbox_handler,
1642                   I(CONF_no_remote_resize));
1643     ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1644                   HELPCTX(features_altscreen),
1645                   conf_checkbox_handler, I(CONF_no_alt_screen));
1646     ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1647                   HELPCTX(features_retitle),
1648                   conf_checkbox_handler,
1649                   I(CONF_no_remote_wintitle));
1650     ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1651                       HELPCTX(features_qtitle),
1652                       conf_radiobutton_handler,
1653                       I(CONF_remote_qtitle_action),
1654                       "None", I(TITLE_NONE),
1655                       "Empty string", I(TITLE_EMPTY),
1656                       "Window title", I(TITLE_REAL), NULL);
1657     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1658                   HELPCTX(features_dbackspace),
1659                   conf_checkbox_handler, I(CONF_no_dbackspace));
1660     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1661                   'r', HELPCTX(features_charset), conf_checkbox_handler,
1662                   I(CONF_no_remote_charset));
1663     ctrl_checkbox(s, "Disable Arabic text shaping",
1664                   'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1665                   I(CONF_arabicshaping));
1666     ctrl_checkbox(s, "Disable bidirectional text display",
1667                   'd', HELPCTX(features_bidi), conf_checkbox_handler,
1668                   I(CONF_bidi));
1669
1670     /*
1671      * The Window panel.
1672      */
1673     str = dupprintf("Options controlling %s's window", appname);
1674     ctrl_settitle(b, "Window", str);
1675     sfree(str);
1676
1677     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1678     ctrl_columns(s, 2, 50, 50);
1679     c = ctrl_editbox(s, "Columns", 'm', 100,
1680                      HELPCTX(window_size),
1681                      conf_editbox_handler, I(CONF_width), I(-1));
1682     c->generic.column = 0;
1683     c = ctrl_editbox(s, "Rows", 'r', 100,
1684                      HELPCTX(window_size),
1685                      conf_editbox_handler, I(CONF_height),I(-1));
1686     c->generic.column = 1;
1687     ctrl_columns(s, 1, 100);
1688
1689     s = ctrl_getset(b, "Window", "scrollback",
1690                     "Control the scrollback in the window");
1691     ctrl_editbox(s, "Lines of scrollback", 's', 50,
1692                  HELPCTX(window_scrollback),
1693                  conf_editbox_handler, I(CONF_savelines), I(-1));
1694     ctrl_checkbox(s, "Display scrollbar", 'd',
1695                   HELPCTX(window_scrollback),
1696                   conf_checkbox_handler, I(CONF_scrollbar));
1697     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1698                   HELPCTX(window_scrollback),
1699                   conf_checkbox_handler, I(CONF_scroll_on_key));
1700     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1701                   HELPCTX(window_scrollback),
1702                   conf_checkbox_handler, I(CONF_scroll_on_disp));
1703     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1704                   HELPCTX(window_erased),
1705                   conf_checkbox_handler,
1706                   I(CONF_erase_to_scrollback));
1707
1708     /*
1709      * The Window/Appearance panel.
1710      */
1711     str = dupprintf("Configure the appearance of %s's window", appname);
1712     ctrl_settitle(b, "Window/Appearance", str);
1713     sfree(str);
1714
1715     s = ctrl_getset(b, "Window/Appearance", "cursor",
1716                     "Adjust the use of the cursor");
1717     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1718                       HELPCTX(appearance_cursor),
1719                       conf_radiobutton_handler,
1720                       I(CONF_cursor_type),
1721                       "Block", 'l', I(0),
1722                       "Underline", 'u', I(1),
1723                       "Vertical line", 'v', I(2), NULL);
1724     ctrl_checkbox(s, "Cursor blinks", 'b',
1725                   HELPCTX(appearance_cursor),
1726                   conf_checkbox_handler, I(CONF_blink_cur));
1727
1728     s = ctrl_getset(b, "Window/Appearance", "font",
1729                     "Font settings");
1730     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1731                  HELPCTX(appearance_font),
1732                  conf_fontsel_handler, I(CONF_font));
1733
1734     s = ctrl_getset(b, "Window/Appearance", "mouse",
1735                     "Adjust the use of the mouse pointer");
1736     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1737                   HELPCTX(appearance_hidemouse),
1738                   conf_checkbox_handler, I(CONF_hide_mouseptr));
1739
1740     s = ctrl_getset(b, "Window/Appearance", "border",
1741                     "Adjust the window border");
1742     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1743                  HELPCTX(appearance_border),
1744                  conf_editbox_handler,
1745                  I(CONF_window_border), I(-1));
1746
1747     /*
1748      * The Window/Behaviour panel.
1749      */
1750     str = dupprintf("Configure the behaviour of %s's window", appname);
1751     ctrl_settitle(b, "Window/Behaviour", str);
1752     sfree(str);
1753
1754     s = ctrl_getset(b, "Window/Behaviour", "title",
1755                     "Adjust the behaviour of the window title");
1756     ctrl_editbox(s, "Window title:", 't', 100,
1757                  HELPCTX(appearance_title),
1758                  conf_editbox_handler, I(CONF_wintitle), I(1));
1759     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1760                   HELPCTX(appearance_title),
1761                   conf_checkbox_handler,
1762                   I(CHECKBOX_INVERT | CONF_win_name_always));
1763
1764     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1765     ctrl_checkbox(s, "Warn before closing window", 'w',
1766                   HELPCTX(behaviour_closewarn),
1767                   conf_checkbox_handler, I(CONF_warn_on_close));
1768
1769     /*
1770      * The Window/Translation panel.
1771      */
1772     ctrl_settitle(b, "Window/Translation",
1773                   "Options controlling character set translation");
1774
1775     s = ctrl_getset(b, "Window/Translation", "trans",
1776                     "Character set translation");
1777     ctrl_combobox(s, "Remote character set:",
1778                   'r', 100, HELPCTX(translation_codepage),
1779                   codepage_handler, P(NULL), P(NULL));
1780
1781     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1782     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1783                   HELPCTX(translation_cjk_ambig_wide),
1784                   conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1785
1786     str = dupprintf("Adjust how %s handles line drawing characters", appname);
1787     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1788     sfree(str);
1789     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1790                       HELPCTX(translation_linedraw),
1791                       conf_radiobutton_handler,
1792                       I(CONF_vtmode),
1793                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1794                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1795                       NULL);
1796     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1797                   HELPCTX(selection_linedraw),
1798                   conf_checkbox_handler, I(CONF_rawcnp));
1799
1800     /*
1801      * The Window/Selection panel.
1802      */
1803     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1804         
1805     s = ctrl_getset(b, "Window/Selection", "mouse",
1806                     "Control use of mouse");
1807     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1808                   HELPCTX(selection_shiftdrag),
1809                   conf_checkbox_handler, I(CONF_mouse_override));
1810     ctrl_radiobuttons(s,
1811                       "Default selection mode (Alt+drag does the other one):",
1812                       NO_SHORTCUT, 2,
1813                       HELPCTX(selection_rect),
1814                       conf_radiobutton_handler,
1815                       I(CONF_rect_select),
1816                       "Normal", 'n', I(0),
1817                       "Rectangular block", 'r', I(1), NULL);
1818
1819     s = ctrl_getset(b, "Window/Selection", "charclass",
1820                     "Control the select-one-word-at-a-time mode");
1821     ccd = (struct charclass_data *)
1822         ctrl_alloc(b, sizeof(struct charclass_data));
1823     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1824                                 HELPCTX(selection_charclasses),
1825                                 charclass_handler, P(ccd));
1826     ccd->listbox->listbox.multisel = 1;
1827     ccd->listbox->listbox.ncols = 4;
1828     ccd->listbox->listbox.percentages = snewn(4, int);
1829     ccd->listbox->listbox.percentages[0] = 15;
1830     ccd->listbox->listbox.percentages[1] = 25;
1831     ccd->listbox->listbox.percentages[2] = 20;
1832     ccd->listbox->listbox.percentages[3] = 40;
1833     ctrl_columns(s, 2, 67, 33);
1834     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1835                                 HELPCTX(selection_charclasses),
1836                                 charclass_handler, P(ccd), P(NULL));
1837     ccd->editbox->generic.column = 0;
1838     ccd->button = ctrl_pushbutton(s, "Set", 's',
1839                                   HELPCTX(selection_charclasses),
1840                                   charclass_handler, P(ccd));
1841     ccd->button->generic.column = 1;
1842     ctrl_columns(s, 1, 100);
1843
1844     /*
1845      * The Window/Colours panel.
1846      */
1847     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1848
1849     s = ctrl_getset(b, "Window/Colours", "general",
1850                     "General options for colour usage");
1851     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1852                   HELPCTX(colours_ansi),
1853                   conf_checkbox_handler, I(CONF_ansi_colour));
1854     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1855                   HELPCTX(colours_xterm256), conf_checkbox_handler,
1856                   I(CONF_xterm_256_colour));
1857     ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
1858                       HELPCTX(colours_bold),
1859                       conf_radiobutton_handler, I(CONF_bold_style),
1860                       "The font", I(1),
1861                       "The colour", I(2),
1862                       "Both", I(3),
1863                       NULL);
1864
1865     str = dupprintf("Adjust the precise colours %s displays", appname);
1866     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1867     sfree(str);
1868     ctrl_text(s, "Select a colour from the list, and then click the"
1869               " Modify button to change its appearance.",
1870               HELPCTX(colours_config));
1871     ctrl_columns(s, 2, 67, 33);
1872     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1873     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1874                                HELPCTX(colours_config), colour_handler, P(cd));
1875     cd->listbox->generic.column = 0;
1876     cd->listbox->listbox.height = 7;
1877     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1878     c->generic.column = 1;
1879     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1880                              colour_handler, P(cd), P(NULL));
1881     cd->redit->generic.column = 1;
1882     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1883                              colour_handler, P(cd), P(NULL));
1884     cd->gedit->generic.column = 1;
1885     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1886                              colour_handler, P(cd), P(NULL));
1887     cd->bedit->generic.column = 1;
1888     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1889                                  colour_handler, P(cd));
1890     cd->button->generic.column = 1;
1891     ctrl_columns(s, 1, 100);
1892
1893     /*
1894      * The Connection panel. This doesn't show up if we're in a
1895      * non-network utility such as pterm. We tell this by being
1896      * passed a protocol < 0.
1897      */
1898     if (protocol >= 0) {
1899         ctrl_settitle(b, "Connection", "Options controlling the connection");
1900
1901         s = ctrl_getset(b, "Connection", "keepalive",
1902                         "Sending of null packets to keep session active");
1903         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1904                      HELPCTX(connection_keepalive),
1905                      conf_editbox_handler, I(CONF_ping_interval),
1906                      I(-1));
1907
1908         if (!midsession) {
1909             s = ctrl_getset(b, "Connection", "tcp",
1910                             "Low-level TCP connection options");
1911             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1912                           'n', HELPCTX(connection_nodelay),
1913                           conf_checkbox_handler,
1914                           I(CONF_tcp_nodelay));
1915             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1916                           'p', HELPCTX(connection_tcpkeepalive),
1917                           conf_checkbox_handler,
1918                           I(CONF_tcp_keepalives));
1919 #ifndef NO_IPV6
1920             s = ctrl_getset(b, "Connection", "ipversion",
1921                           "Internet protocol version");
1922             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1923                           HELPCTX(connection_ipversion),
1924                           conf_radiobutton_handler,
1925                           I(CONF_addressfamily),
1926                           "Auto", 'u', I(ADDRTYPE_UNSPEC),
1927                           "IPv4", '4', I(ADDRTYPE_IPV4),
1928                           "IPv6", '6', I(ADDRTYPE_IPV6),
1929                           NULL);
1930 #endif
1931
1932             {
1933                 const char *label = backend_from_proto(PROT_SSH) ?
1934                     "Logical name of remote host (e.g. for SSH key lookup):" :
1935                     "Logical name of remote host:";
1936                 s = ctrl_getset(b, "Connection", "identity",
1937                                 "Logical name of remote host");
1938                 ctrl_editbox(s, label, 'm', 100,
1939                              HELPCTX(connection_loghost),
1940                              conf_editbox_handler, I(CONF_loghost), I(1));
1941             }
1942         }
1943
1944         /*
1945          * A sub-panel Connection/Data, containing options that
1946          * decide on data to send to the server.
1947          */
1948         if (!midsession) {
1949             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1950
1951             s = ctrl_getset(b, "Connection/Data", "login",
1952                             "Login details");
1953             ctrl_editbox(s, "Auto-login username", 'u', 50,
1954                          HELPCTX(connection_username),
1955                          conf_editbox_handler, I(CONF_username), I(1));
1956             {
1957                 /* We assume the local username is sufficiently stable
1958                  * to include on the dialog box. */
1959                 char *user = get_username();
1960                 char *userlabel = dupprintf("Use system username (%s)",
1961                                             user ? user : "");
1962                 sfree(user);
1963                 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1964                                   HELPCTX(connection_username_from_env),
1965                                   conf_radiobutton_handler,
1966                                   I(CONF_username_from_env),
1967                                   "Prompt", I(FALSE),
1968                                   userlabel, I(TRUE),
1969                                   NULL);
1970                 sfree(userlabel);
1971             }
1972
1973             s = ctrl_getset(b, "Connection/Data", "term",
1974                             "Terminal details");
1975             ctrl_editbox(s, "Terminal-type string", 't', 50,
1976                          HELPCTX(connection_termtype),
1977                          conf_editbox_handler, I(CONF_termtype), I(1));
1978             ctrl_editbox(s, "Terminal speeds", 's', 50,
1979                          HELPCTX(connection_termspeed),
1980                          conf_editbox_handler, I(CONF_termspeed), I(1));
1981
1982             s = ctrl_getset(b, "Connection/Data", "env",
1983                             "Environment variables");
1984             ctrl_columns(s, 2, 80, 20);
1985             ed = (struct environ_data *)
1986                 ctrl_alloc(b, sizeof(struct environ_data));
1987             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1988                                       HELPCTX(telnet_environ),
1989                                       environ_handler, P(ed), P(NULL));
1990             ed->varbox->generic.column = 0;
1991             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1992                                       HELPCTX(telnet_environ),
1993                                       environ_handler, P(ed), P(NULL));
1994             ed->valbox->generic.column = 0;
1995             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1996                                             HELPCTX(telnet_environ),
1997                                             environ_handler, P(ed));
1998             ed->addbutton->generic.column = 1;
1999             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2000                                             HELPCTX(telnet_environ),
2001                                             environ_handler, P(ed));
2002             ed->rembutton->generic.column = 1;
2003             ctrl_columns(s, 1, 100);
2004             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2005                                        HELPCTX(telnet_environ),
2006                                        environ_handler, P(ed));
2007             ed->listbox->listbox.height = 3;
2008             ed->listbox->listbox.ncols = 2;
2009             ed->listbox->listbox.percentages = snewn(2, int);
2010             ed->listbox->listbox.percentages[0] = 30;
2011             ed->listbox->listbox.percentages[1] = 70;
2012         }
2013
2014     }
2015
2016     if (!midsession) {
2017         /*
2018          * The Connection/Proxy panel.
2019          */
2020         ctrl_settitle(b, "Connection/Proxy",
2021                       "Options controlling proxy usage");
2022
2023         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
2024         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
2025                           HELPCTX(proxy_type),
2026                           conf_radiobutton_handler,
2027                           I(CONF_proxy_type),
2028                           "None", I(PROXY_NONE),
2029                           "SOCKS 4", I(PROXY_SOCKS4),
2030                           "SOCKS 5", I(PROXY_SOCKS5),
2031                           "HTTP", I(PROXY_HTTP),
2032                           "Telnet", I(PROXY_TELNET),
2033                           NULL);
2034         ctrl_columns(s, 2, 80, 20);
2035         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
2036                          HELPCTX(proxy_main),
2037                          conf_editbox_handler,
2038                          I(CONF_proxy_host), I(1));
2039         c->generic.column = 0;
2040         c = ctrl_editbox(s, "Port", 'p', 100,
2041                          HELPCTX(proxy_main),
2042                          conf_editbox_handler,
2043                          I(CONF_proxy_port),
2044                          I(-1));
2045         c->generic.column = 1;
2046         ctrl_columns(s, 1, 100);
2047         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
2048                      HELPCTX(proxy_exclude),
2049                      conf_editbox_handler,
2050                      I(CONF_proxy_exclude_list), I(1));
2051         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
2052                       HELPCTX(proxy_exclude),
2053                       conf_checkbox_handler,
2054                       I(CONF_even_proxy_localhost));
2055         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
2056                           HELPCTX(proxy_dns),
2057                           conf_radiobutton_handler,
2058                           I(CONF_proxy_dns),
2059                           "No", I(FORCE_OFF),
2060                           "Auto", I(AUTO),
2061                           "Yes", I(FORCE_ON), NULL);
2062         ctrl_editbox(s, "Username", 'u', 60,
2063                      HELPCTX(proxy_auth),
2064                      conf_editbox_handler,
2065                      I(CONF_proxy_username), I(1));
2066         c = ctrl_editbox(s, "Password", 'w', 60,
2067                          HELPCTX(proxy_auth),
2068                          conf_editbox_handler,
2069                          I(CONF_proxy_password), I(1));
2070         c->editbox.password = 1;
2071         ctrl_editbox(s, "Telnet command", 'm', 100,
2072                      HELPCTX(proxy_command),
2073                      conf_editbox_handler,
2074                      I(CONF_proxy_telnet_command), I(1));
2075
2076         ctrl_radiobuttons(s, "Print proxy diagnostics "
2077                           "in the terminal window", 'r', 5,
2078                           HELPCTX(proxy_main),
2079                           conf_radiobutton_handler,
2080                           I(CONF_proxy_log_to_term),
2081                           "No", I(FORCE_OFF),
2082                           "Yes", I(FORCE_ON),
2083                           "Only until session starts", I(AUTO), NULL);
2084     }
2085
2086     /*
2087      * The Telnet panel exists in the base config box, and in a
2088      * mid-session reconfig box _if_ we're using Telnet.
2089      */
2090     if (!midsession || protocol == PROT_TELNET) {
2091         /*
2092          * The Connection/Telnet panel.
2093          */
2094         ctrl_settitle(b, "Connection/Telnet",
2095                       "Options controlling Telnet connections");
2096
2097         s = ctrl_getset(b, "Connection/Telnet", "protocol",
2098                         "Telnet protocol adjustments");
2099
2100         if (!midsession) {
2101             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2102                               NO_SHORTCUT, 2,
2103                               HELPCTX(telnet_oldenviron),
2104                               conf_radiobutton_handler,
2105                               I(CONF_rfc_environ),
2106                               "BSD (commonplace)", 'b', I(0),
2107                               "RFC 1408 (unusual)", 'f', I(1), NULL);
2108             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2109                               HELPCTX(telnet_passive),
2110                               conf_radiobutton_handler,
2111                               I(CONF_passive_telnet),
2112                               "Passive", I(1), "Active", I(0), NULL);
2113         }
2114         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2115                       HELPCTX(telnet_specialkeys),
2116                       conf_checkbox_handler,
2117                       I(CONF_telnet_keyboard));
2118         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2119                       'm', HELPCTX(telnet_newline),
2120                       conf_checkbox_handler,
2121                       I(CONF_telnet_newline));
2122     }
2123
2124     if (!midsession) {
2125
2126         /*
2127          * The Connection/Rlogin panel.
2128          */
2129         ctrl_settitle(b, "Connection/Rlogin",
2130                       "Options controlling Rlogin connections");
2131
2132         s = ctrl_getset(b, "Connection/Rlogin", "data",
2133                         "Data to send to the server");
2134         ctrl_editbox(s, "Local username:", 'l', 50,
2135                      HELPCTX(rlogin_localuser),
2136                      conf_editbox_handler, I(CONF_localusername), I(1));
2137
2138     }
2139
2140     /*
2141      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2142      * when we're not doing SSH.
2143      */
2144
2145     if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2146
2147         /*
2148          * The Connection/SSH panel.
2149          */
2150         ctrl_settitle(b, "Connection/SSH",
2151                       "Options controlling SSH connections");
2152
2153         /* SSH-1 or connection-sharing downstream */
2154         if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
2155             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2156             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2157                       "session; it is only here so that sub-panels of it can "
2158                       "exist without looking strange.", HELPCTX(no_help));
2159         }
2160
2161         if (!midsession) {
2162
2163             s = ctrl_getset(b, "Connection/SSH", "data",
2164                             "Data to send to the server");
2165             ctrl_editbox(s, "Remote command:", 'r', 100,
2166                          HELPCTX(ssh_command),
2167                          conf_editbox_handler, I(CONF_remote_cmd), I(1));
2168
2169             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2170             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2171                           HELPCTX(ssh_noshell),
2172                           conf_checkbox_handler,
2173                           I(CONF_ssh_no_shell));
2174         }
2175
2176         if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2177             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2178
2179             ctrl_checkbox(s, "Enable compression", 'e',
2180                           HELPCTX(ssh_compress),
2181                           conf_checkbox_handler,
2182                           I(CONF_compression));
2183         }
2184
2185         if (!midsession) {
2186             s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
2187
2188             ctrl_checkbox(s, "Share SSH connections if possible", 's',
2189                           HELPCTX(ssh_share),
2190                           conf_checkbox_handler,
2191                           I(CONF_ssh_connection_sharing));
2192
2193             ctrl_text(s, "Permitted roles in a shared connection:",
2194                       HELPCTX(ssh_share));
2195             ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
2196                           HELPCTX(ssh_share),
2197                           conf_checkbox_handler,
2198                           I(CONF_ssh_connection_sharing_upstream));
2199             ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
2200                           HELPCTX(ssh_share),
2201                           conf_checkbox_handler,
2202                           I(CONF_ssh_connection_sharing_downstream));
2203         }
2204
2205         if (!midsession) {
2206             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2207
2208             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2209                               HELPCTX(ssh_protocol),
2210                               conf_radiobutton_handler,
2211                               I(CONF_sshprot),
2212                               "1 only", 'l', I(0),
2213                               "1", '1', I(1),
2214                               "2", '2', I(2),
2215                               "2 only", 'y', I(3), NULL);
2216         }
2217
2218         /*
2219          * The Connection/SSH/Kex panel. (Owing to repeat key
2220          * exchange, much of this is meaningful in mid-session _if_
2221          * we're using SSH-2 and are not a connection-sharing
2222          * downstream, or haven't decided yet.)
2223          */
2224         if (protcfginfo != 1 && protcfginfo != -1) {
2225             ctrl_settitle(b, "Connection/SSH/Kex",
2226                           "Options controlling SSH key exchange");
2227
2228             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2229                             "Key exchange algorithm options");
2230             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2231                               HELPCTX(ssh_kexlist),
2232                               kexlist_handler, P(NULL));
2233             c->listbox.height = 5;
2234
2235             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2236                             "Options controlling key re-exchange");
2237
2238             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2239                          HELPCTX(ssh_kex_repeat),
2240                          conf_editbox_handler,
2241                          I(CONF_ssh_rekey_time),
2242                          I(-1));
2243             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2244                          HELPCTX(ssh_kex_repeat),
2245                          conf_editbox_handler,
2246                          I(CONF_ssh_rekey_data),
2247                          I(16));
2248             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2249                       HELPCTX(ssh_kex_repeat));
2250         }
2251
2252         /*
2253          * Manual host key configuration is irrelevant mid-session,
2254          * as we enforce that the host key for rekeys is the
2255          * same as that used at the start of the session.
2256          */
2257         if (!midsession) {
2258             s = ctrl_getset(b, "Connection/SSH/Kex", "hostkeys",
2259                             "Manually configure host keys for this connection");
2260
2261             ctrl_columns(s, 2, 75, 25);
2262             c = ctrl_text(s, "Host keys or fingerprints to accept:",
2263                           HELPCTX(ssh_kex_manual_hostkeys));
2264             c->generic.column = 0;
2265             /* You want to select from the list, _then_ hit Remove. So
2266              * tab order should be that way round. */
2267             mh = (struct manual_hostkey_data *)
2268                 ctrl_alloc(b,sizeof(struct manual_hostkey_data));
2269             mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2270                                             HELPCTX(ssh_kex_manual_hostkeys),
2271                                             manual_hostkey_handler, P(mh));
2272             mh->rembutton->generic.column = 1;
2273             mh->rembutton->generic.tabdelay = 1;
2274             mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2275                                        HELPCTX(ssh_kex_manual_hostkeys),
2276                                        manual_hostkey_handler, P(mh));
2277             /* This list box can't be very tall, because there's not
2278              * much room in the pane on Windows at least. This makes
2279              * it become really unhelpful if a horizontal scrollbar
2280              * appears, so we suppress that. */
2281             mh->listbox->listbox.height = 2;
2282             mh->listbox->listbox.hscroll = FALSE;
2283             ctrl_tabdelay(s, mh->rembutton);
2284             mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
2285                                       HELPCTX(ssh_kex_manual_hostkeys),
2286                                       manual_hostkey_handler, P(mh), P(NULL));
2287             mh->keybox->generic.column = 0;
2288             mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
2289                                             HELPCTX(ssh_kex_manual_hostkeys),
2290                                             manual_hostkey_handler, P(mh));
2291             mh->addbutton->generic.column = 1;
2292             ctrl_columns(s, 1, 100);
2293         }
2294
2295         if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2296             /*
2297              * The Connection/SSH/Cipher panel.
2298              */
2299             ctrl_settitle(b, "Connection/SSH/Cipher",
2300                           "Options controlling SSH encryption");
2301
2302             s = ctrl_getset(b, "Connection/SSH/Cipher",
2303                             "encryption", "Encryption options");
2304             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2305                               HELPCTX(ssh_ciphers),
2306                               cipherlist_handler, P(NULL));
2307             c->listbox.height = 6;
2308
2309             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2310                           HELPCTX(ssh_ciphers),
2311                           conf_checkbox_handler,
2312                           I(CONF_ssh2_des_cbc));
2313         }
2314
2315         if (!midsession) {
2316
2317             /*
2318              * The Connection/SSH/Auth panel.
2319              */
2320             ctrl_settitle(b, "Connection/SSH/Auth",
2321                           "Options controlling SSH authentication");
2322
2323             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2324             ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2325                           'd', HELPCTX(ssh_auth_banner),
2326                           conf_checkbox_handler,
2327                           I(CONF_ssh_show_banner));
2328             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2329                           HELPCTX(ssh_auth_bypass),
2330                           conf_checkbox_handler,
2331                           I(CONF_ssh_no_userauth));
2332
2333             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2334                             "Authentication methods");
2335             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2336                           HELPCTX(ssh_auth_pageant),
2337                           conf_checkbox_handler,
2338                           I(CONF_tryagent));
2339             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2340                           HELPCTX(ssh_auth_tis),
2341                           conf_checkbox_handler,
2342                           I(CONF_try_tis_auth));
2343             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2344                           'i', HELPCTX(ssh_auth_ki),
2345                           conf_checkbox_handler,
2346                           I(CONF_try_ki_auth));
2347
2348             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2349                             "Authentication parameters");
2350             ctrl_checkbox(s, "Allow agent forwarding", 'f',
2351                           HELPCTX(ssh_auth_agentfwd),
2352                           conf_checkbox_handler, I(CONF_agentfwd));
2353             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2354                           HELPCTX(ssh_auth_changeuser),
2355                           conf_checkbox_handler,
2356                           I(CONF_change_username));
2357             ctrl_filesel(s, "Private key file for authentication:", 'k',
2358                          FILTER_KEY_FILES, FALSE, "Select private key file",
2359                          HELPCTX(ssh_auth_privkey),
2360                          conf_filesel_handler, I(CONF_keyfile));
2361
2362 #ifndef NO_GSSAPI
2363             /*
2364              * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2365              * the main Auth panel.
2366              */
2367             ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2368                           "Options controlling GSSAPI authentication");
2369             s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2370
2371             ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2372                           't', HELPCTX(ssh_gssapi),
2373                           conf_checkbox_handler,
2374                           I(CONF_try_gssapi_auth));
2375
2376             ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2377                           HELPCTX(ssh_gssapi_delegation),
2378                           conf_checkbox_handler,
2379                           I(CONF_gssapifwd));
2380
2381             /*
2382              * GSSAPI library selection.
2383              */
2384             if (ngsslibs > 1) {
2385                 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2386                                   'p', HELPCTX(ssh_gssapi_libraries),
2387                                   gsslist_handler, P(NULL));
2388                 c->listbox.height = ngsslibs;
2389
2390                 /*
2391                  * I currently assume that if more than one GSS
2392                  * library option is available, then one of them is
2393                  * 'user-supplied' and so we should present the
2394                  * following file selector. This is at least half-
2395                  * reasonable, because if we're using statically
2396                  * linked GSSAPI then there will only be one option
2397                  * and no way to load from a user-supplied library,
2398                  * whereas if we're using dynamic libraries then
2399                  * there will almost certainly be some default
2400                  * option in addition to a user-supplied path. If
2401                  * anyone ever ports PuTTY to a system on which
2402                  * dynamic-library GSSAPI is available but there is
2403                  * absolutely no consensus on where to keep the
2404                  * libraries, there'll need to be a flag alongside
2405                  * ngsslibs to control whether the file selector is
2406                  * displayed. 
2407                  */
2408
2409                 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2410                              FILTER_DYNLIB_FILES, FALSE, "Select library file",
2411                              HELPCTX(ssh_gssapi_libraries),
2412                              conf_filesel_handler,
2413                              I(CONF_ssh_gss_custom));
2414             }
2415 #endif
2416         }
2417
2418         if (!midsession) {
2419             /*
2420              * The Connection/SSH/TTY panel.
2421              */
2422             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2423
2424             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2425             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2426                           HELPCTX(ssh_nopty),
2427                           conf_checkbox_handler,
2428                           I(CONF_nopty));
2429
2430             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2431                             "Terminal modes");
2432             td = (struct ttymodes_data *)
2433                 ctrl_alloc(b, sizeof(struct ttymodes_data));
2434             ctrl_columns(s, 2, 75, 25);
2435             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2436             c->generic.column = 0;
2437             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2438                                             HELPCTX(ssh_ttymodes),
2439                                             ttymodes_handler, P(td));
2440             td->rembutton->generic.column = 1;
2441             td->rembutton->generic.tabdelay = 1;
2442             ctrl_columns(s, 1, 100);
2443             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2444                                        HELPCTX(ssh_ttymodes),
2445                                        ttymodes_handler, P(td));
2446             td->listbox->listbox.multisel = 1;
2447             td->listbox->listbox.height = 4;
2448             td->listbox->listbox.ncols = 2;
2449             td->listbox->listbox.percentages = snewn(2, int);
2450             td->listbox->listbox.percentages[0] = 40;
2451             td->listbox->listbox.percentages[1] = 60;
2452             ctrl_tabdelay(s, td->rembutton);
2453             ctrl_columns(s, 2, 75, 25);
2454             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2455                                          HELPCTX(ssh_ttymodes),
2456                                          ttymodes_handler, P(td));
2457             td->modelist->generic.column = 0;
2458             td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2459                                             HELPCTX(ssh_ttymodes),
2460                                             ttymodes_handler, P(td));
2461             td->addbutton->generic.column = 1;
2462             td->addbutton->generic.tabdelay = 1;
2463             ctrl_columns(s, 1, 100);        /* column break */
2464             /* Bit of a hack to get the value radio buttons and
2465              * edit-box on the same row. */
2466             ctrl_columns(s, 3, 25, 50, 25);
2467             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2468             c->generic.column = 0;
2469             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2470                                              HELPCTX(ssh_ttymodes),
2471                                              ttymodes_handler, P(td),
2472                                              "Auto", NO_SHORTCUT, P(NULL),
2473                                              "This:", NO_SHORTCUT, P(NULL),
2474                                              NULL);
2475             td->valradio->generic.column = 1;
2476             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2477                                       HELPCTX(ssh_ttymodes),
2478                                       ttymodes_handler, P(td), P(NULL));
2479             td->valbox->generic.column = 2;
2480             ctrl_tabdelay(s, td->addbutton);
2481
2482         }
2483
2484         if (!midsession) {
2485             /*
2486              * The Connection/SSH/X11 panel.
2487              */
2488             ctrl_settitle(b, "Connection/SSH/X11",
2489                           "Options controlling SSH X11 forwarding");
2490
2491             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2492             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2493                           HELPCTX(ssh_tunnels_x11),
2494                           conf_checkbox_handler,I(CONF_x11_forward));
2495             ctrl_editbox(s, "X display location", 'x', 50,
2496                          HELPCTX(ssh_tunnels_x11),
2497                          conf_editbox_handler, I(CONF_x11_display), I(1));
2498             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2499                               HELPCTX(ssh_tunnels_x11auth),
2500                               conf_radiobutton_handler,
2501                               I(CONF_x11_auth),
2502                               "MIT-Magic-Cookie-1", I(X11_MIT),
2503                               "XDM-Authorization-1", I(X11_XDM), NULL);
2504         }
2505
2506         /*
2507          * The Tunnels panel _is_ still available in mid-session.
2508          */
2509         ctrl_settitle(b, "Connection/SSH/Tunnels",
2510                       "Options controlling SSH port forwarding");
2511
2512         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2513                         "Port forwarding");
2514         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2515                       HELPCTX(ssh_tunnels_portfwd_localhost),
2516                       conf_checkbox_handler,
2517                       I(CONF_lport_acceptall));
2518         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2519                       HELPCTX(ssh_tunnels_portfwd_localhost),
2520                       conf_checkbox_handler,
2521                       I(CONF_rport_acceptall));
2522
2523         ctrl_columns(s, 3, 55, 20, 25);
2524         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2525         c->generic.column = COLUMN_FIELD(0,2);
2526         /* You want to select from the list, _then_ hit Remove. So tab order
2527          * should be that way round. */
2528         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2529         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2530                                          HELPCTX(ssh_tunnels_portfwd),
2531                                          portfwd_handler, P(pfd));
2532         pfd->rembutton->generic.column = 2;
2533         pfd->rembutton->generic.tabdelay = 1;
2534         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2535                                     HELPCTX(ssh_tunnels_portfwd),
2536                                     portfwd_handler, P(pfd));
2537         pfd->listbox->listbox.height = 3;
2538         pfd->listbox->listbox.ncols = 2;
2539         pfd->listbox->listbox.percentages = snewn(2, int);
2540         pfd->listbox->listbox.percentages[0] = 20;
2541         pfd->listbox->listbox.percentages[1] = 80;
2542         ctrl_tabdelay(s, pfd->rembutton);
2543         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2544         /* You want to enter source, destination and type, _then_ hit Add.
2545          * Again, we adjust the tab order to reflect this. */
2546         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2547                                          HELPCTX(ssh_tunnels_portfwd),
2548                                          portfwd_handler, P(pfd));
2549         pfd->addbutton->generic.column = 2;
2550         pfd->addbutton->generic.tabdelay = 1;
2551         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2552                                       HELPCTX(ssh_tunnels_portfwd),
2553                                       portfwd_handler, P(pfd), P(NULL));
2554         pfd->sourcebox->generic.column = 0;
2555         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2556                                     HELPCTX(ssh_tunnels_portfwd),
2557                                     portfwd_handler, P(pfd), P(NULL));
2558         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2559                                            HELPCTX(ssh_tunnels_portfwd),
2560                                            portfwd_handler, P(pfd),
2561                                            "Local", 'l', P(NULL),
2562                                            "Remote", 'm', P(NULL),
2563                                            "Dynamic", 'y', P(NULL),
2564                                            NULL);
2565 #ifndef NO_IPV6
2566         pfd->addressfamily =
2567             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2568                               HELPCTX(ssh_tunnels_portfwd_ipversion),
2569                               portfwd_handler, P(pfd),
2570                               "Auto", 'u', I(ADDRTYPE_UNSPEC),
2571                               "IPv4", '4', I(ADDRTYPE_IPV4),
2572                               "IPv6", '6', I(ADDRTYPE_IPV6),
2573                               NULL);
2574 #endif
2575         ctrl_tabdelay(s, pfd->addbutton);
2576         ctrl_columns(s, 1, 100);
2577
2578         if (!midsession) {
2579             /*
2580              * The Connection/SSH/Bugs panels.
2581              */
2582             ctrl_settitle(b, "Connection/SSH/Bugs",
2583                           "Workarounds for SSH server bugs");
2584
2585             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2586                             "Detection of known bugs in SSH servers");
2587             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2588                           HELPCTX(ssh_bugs_ignore1),
2589                           sshbug_handler, I(CONF_sshbug_ignore1));
2590             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2591                           HELPCTX(ssh_bugs_plainpw1),
2592                           sshbug_handler, I(CONF_sshbug_plainpw1));
2593             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2594                           HELPCTX(ssh_bugs_rsa1),
2595                           sshbug_handler, I(CONF_sshbug_rsa1));
2596             ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2597                           HELPCTX(ssh_bugs_ignore2),
2598                           sshbug_handler, I(CONF_sshbug_ignore2));
2599             ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
2600                           20, HELPCTX(ssh_bugs_winadj),
2601                           sshbug_handler, I(CONF_sshbug_winadj));
2602             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2603                           HELPCTX(ssh_bugs_hmac2),
2604                           sshbug_handler, I(CONF_sshbug_hmac2));
2605             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2606                           HELPCTX(ssh_bugs_derivekey2),
2607                           sshbug_handler, I(CONF_sshbug_derivekey2));
2608
2609             ctrl_settitle(b, "Connection/SSH/More bugs",
2610                           "Further workarounds for SSH server bugs");
2611
2612             s = ctrl_getset(b, "Connection/SSH/More bugs", "main",
2613                             "Detection of known bugs in SSH servers");
2614             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2615                           HELPCTX(ssh_bugs_rsapad2),
2616                           sshbug_handler, I(CONF_sshbug_rsapad2));
2617             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2618                           HELPCTX(ssh_bugs_pksessid2),
2619                           sshbug_handler, I(CONF_sshbug_pksessid2));
2620             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2621                           HELPCTX(ssh_bugs_rekey2),
2622                           sshbug_handler, I(CONF_sshbug_rekey2));
2623             ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2624                           HELPCTX(ssh_bugs_maxpkt2),
2625                           sshbug_handler, I(CONF_sshbug_maxpkt2));
2626             ctrl_droplist(s, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20,
2627                           HELPCTX(ssh_bugs_oldgex2),
2628                           sshbug_handler, I(CONF_sshbug_oldgex2));
2629             ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
2630                           HELPCTX(ssh_bugs_chanreq),
2631                           sshbug_handler, I(CONF_sshbug_chanreq));
2632         }
2633     }
2634 }