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