]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Merge branch 'master' of ssh://tartarus.org/putty
[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_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1694                       HELPCTX(features_qtitle),
1695                       conf_radiobutton_handler,
1696                       I(CONF_remote_qtitle_action),
1697                       "None", I(TITLE_NONE),
1698                       "Empty string", I(TITLE_EMPTY),
1699                       "Window title", I(TITLE_REAL), NULL);
1700     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1701                   HELPCTX(features_dbackspace),
1702                   conf_checkbox_handler, I(CONF_no_dbackspace));
1703     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1704                   'r', HELPCTX(features_charset), conf_checkbox_handler,
1705                   I(CONF_no_remote_charset));
1706     ctrl_checkbox(s, "Disable Arabic text shaping",
1707                   'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1708                   I(CONF_arabicshaping));
1709     ctrl_checkbox(s, "Disable bidirectional text display",
1710                   'd', HELPCTX(features_bidi), conf_checkbox_handler,
1711                   I(CONF_bidi));
1712
1713     /*
1714      * The Window panel.
1715      */
1716     str = dupprintf("Options controlling %s's window", appname);
1717     ctrl_settitle(b, "Window", str);
1718     sfree(str);
1719
1720     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1721     ctrl_columns(s, 2, 50, 50);
1722     c = ctrl_editbox(s, "Columns", 'm', 100,
1723                      HELPCTX(window_size),
1724                      conf_editbox_handler, I(CONF_width), I(-1));
1725     c->generic.column = 0;
1726     c = ctrl_editbox(s, "Rows", 'r', 100,
1727                      HELPCTX(window_size),
1728                      conf_editbox_handler, I(CONF_height),I(-1));
1729     c->generic.column = 1;
1730     ctrl_columns(s, 1, 100);
1731
1732     s = ctrl_getset(b, "Window", "scrollback",
1733                     "Control the scrollback in the window");
1734     ctrl_editbox(s, "Lines of scrollback", 's', 50,
1735                  HELPCTX(window_scrollback),
1736                  conf_editbox_handler, I(CONF_savelines), I(-1));
1737     ctrl_checkbox(s, "Display scrollbar", 'd',
1738                   HELPCTX(window_scrollback),
1739                   conf_checkbox_handler, I(CONF_scrollbar));
1740     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1741                   HELPCTX(window_scrollback),
1742                   conf_checkbox_handler, I(CONF_scroll_on_key));
1743     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1744                   HELPCTX(window_scrollback),
1745                   conf_checkbox_handler, I(CONF_scroll_on_disp));
1746     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1747                   HELPCTX(window_erased),
1748                   conf_checkbox_handler,
1749                   I(CONF_erase_to_scrollback));
1750
1751     /*
1752      * The Window/Appearance panel.
1753      */
1754     str = dupprintf("Configure the appearance of %s's window", appname);
1755     ctrl_settitle(b, "Window/Appearance", str);
1756     sfree(str);
1757
1758     s = ctrl_getset(b, "Window/Appearance", "cursor",
1759                     "Adjust the use of the cursor");
1760     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1761                       HELPCTX(appearance_cursor),
1762                       conf_radiobutton_handler,
1763                       I(CONF_cursor_type),
1764                       "Block", 'l', I(0),
1765                       "Underline", 'u', I(1),
1766                       "Vertical line", 'v', I(2), NULL);
1767     ctrl_checkbox(s, "Cursor blinks", 'b',
1768                   HELPCTX(appearance_cursor),
1769                   conf_checkbox_handler, I(CONF_blink_cur));
1770
1771     s = ctrl_getset(b, "Window/Appearance", "font",
1772                     "Font settings");
1773     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1774                  HELPCTX(appearance_font),
1775                  conf_fontsel_handler, I(CONF_font));
1776
1777     s = ctrl_getset(b, "Window/Appearance", "mouse",
1778                     "Adjust the use of the mouse pointer");
1779     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1780                   HELPCTX(appearance_hidemouse),
1781                   conf_checkbox_handler, I(CONF_hide_mouseptr));
1782
1783     s = ctrl_getset(b, "Window/Appearance", "border",
1784                     "Adjust the window border");
1785     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1786                  HELPCTX(appearance_border),
1787                  conf_editbox_handler,
1788                  I(CONF_window_border), I(-1));
1789
1790     /*
1791      * The Window/Behaviour panel.
1792      */
1793     str = dupprintf("Configure the behaviour of %s's window", appname);
1794     ctrl_settitle(b, "Window/Behaviour", str);
1795     sfree(str);
1796
1797     s = ctrl_getset(b, "Window/Behaviour", "title",
1798                     "Adjust the behaviour of the window title");
1799     ctrl_editbox(s, "Window title:", 't', 100,
1800                  HELPCTX(appearance_title),
1801                  conf_editbox_handler, I(CONF_wintitle), I(1));
1802     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1803                   HELPCTX(appearance_title),
1804                   conf_checkbox_handler,
1805                   I(CHECKBOX_INVERT | CONF_win_name_always));
1806
1807     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1808     ctrl_checkbox(s, "Warn before closing window", 'w',
1809                   HELPCTX(behaviour_closewarn),
1810                   conf_checkbox_handler, I(CONF_warn_on_close));
1811
1812     /*
1813      * The Window/Translation panel.
1814      */
1815     ctrl_settitle(b, "Window/Translation",
1816                   "Options controlling character set translation");
1817
1818     s = ctrl_getset(b, "Window/Translation", "trans",
1819                     "Character set translation");
1820     ctrl_combobox(s, "Remote character set:",
1821                   'r', 100, HELPCTX(translation_codepage),
1822                   codepage_handler, P(NULL), P(NULL));
1823
1824     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1825     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1826                   HELPCTX(translation_cjk_ambig_wide),
1827                   conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1828
1829     str = dupprintf("Adjust how %s handles line drawing characters", appname);
1830     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1831     sfree(str);
1832     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1833                       HELPCTX(translation_linedraw),
1834                       conf_radiobutton_handler,
1835                       I(CONF_vtmode),
1836                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1837                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1838                       NULL);
1839     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1840                   HELPCTX(selection_linedraw),
1841                   conf_checkbox_handler, I(CONF_rawcnp));
1842
1843     /*
1844      * The Window/Selection panel.
1845      */
1846     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1847         
1848     s = ctrl_getset(b, "Window/Selection", "mouse",
1849                     "Control use of mouse");
1850     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1851                   HELPCTX(selection_shiftdrag),
1852                   conf_checkbox_handler, I(CONF_mouse_override));
1853     ctrl_radiobuttons(s,
1854                       "Default selection mode (Alt+drag does the other one):",
1855                       NO_SHORTCUT, 2,
1856                       HELPCTX(selection_rect),
1857                       conf_radiobutton_handler,
1858                       I(CONF_rect_select),
1859                       "Normal", 'n', I(0),
1860                       "Rectangular block", 'r', I(1), NULL);
1861
1862     s = ctrl_getset(b, "Window/Selection", "charclass",
1863                     "Control the select-one-word-at-a-time mode");
1864     ccd = (struct charclass_data *)
1865         ctrl_alloc(b, sizeof(struct charclass_data));
1866     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1867                                 HELPCTX(selection_charclasses),
1868                                 charclass_handler, P(ccd));
1869     ccd->listbox->listbox.multisel = 1;
1870     ccd->listbox->listbox.ncols = 4;
1871     ccd->listbox->listbox.percentages = snewn(4, int);
1872     ccd->listbox->listbox.percentages[0] = 15;
1873     ccd->listbox->listbox.percentages[1] = 25;
1874     ccd->listbox->listbox.percentages[2] = 20;
1875     ccd->listbox->listbox.percentages[3] = 40;
1876     ctrl_columns(s, 2, 67, 33);
1877     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1878                                 HELPCTX(selection_charclasses),
1879                                 charclass_handler, P(ccd), P(NULL));
1880     ccd->editbox->generic.column = 0;
1881     ccd->button = ctrl_pushbutton(s, "Set", 's',
1882                                   HELPCTX(selection_charclasses),
1883                                   charclass_handler, P(ccd));
1884     ccd->button->generic.column = 1;
1885     ctrl_columns(s, 1, 100);
1886
1887     /*
1888      * The Window/Colours panel.
1889      */
1890     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1891
1892     s = ctrl_getset(b, "Window/Colours", "general",
1893                     "General options for colour usage");
1894     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1895                   HELPCTX(colours_ansi),
1896                   conf_checkbox_handler, I(CONF_ansi_colour));
1897     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1898                   HELPCTX(colours_xterm256), conf_checkbox_handler,
1899                   I(CONF_xterm_256_colour));
1900     ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
1901                       HELPCTX(colours_bold),
1902                       conf_radiobutton_handler, I(CONF_bold_style),
1903                       "The font", I(1),
1904                       "The colour", I(2),
1905                       "Both", I(3),
1906                       NULL);
1907
1908     str = dupprintf("Adjust the precise colours %s displays", appname);
1909     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1910     sfree(str);
1911     ctrl_text(s, "Select a colour from the list, and then click the"
1912               " Modify button to change its appearance.",
1913               HELPCTX(colours_config));
1914     ctrl_columns(s, 2, 67, 33);
1915     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1916     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1917                                HELPCTX(colours_config), colour_handler, P(cd));
1918     cd->listbox->generic.column = 0;
1919     cd->listbox->listbox.height = 7;
1920     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1921     c->generic.column = 1;
1922     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1923                              colour_handler, P(cd), P(NULL));
1924     cd->redit->generic.column = 1;
1925     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1926                              colour_handler, P(cd), P(NULL));
1927     cd->gedit->generic.column = 1;
1928     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1929                              colour_handler, P(cd), P(NULL));
1930     cd->bedit->generic.column = 1;
1931     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1932                                  colour_handler, P(cd));
1933     cd->button->generic.column = 1;
1934     ctrl_columns(s, 1, 100);
1935
1936     /*
1937      * The Connection panel. This doesn't show up if we're in a
1938      * non-network utility such as pterm. We tell this by being
1939      * passed a protocol < 0.
1940      */
1941     if (protocol >= 0) {
1942         ctrl_settitle(b, "Connection", "Options controlling the connection");
1943
1944         s = ctrl_getset(b, "Connection", "keepalive",
1945                         "Sending of null packets to keep session active");
1946         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1947                      HELPCTX(connection_keepalive),
1948                      conf_editbox_handler, I(CONF_ping_interval),
1949                      I(-1));
1950
1951         if (!midsession) {
1952             s = ctrl_getset(b, "Connection", "tcp",
1953                             "Low-level TCP connection options");
1954             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1955                           'n', HELPCTX(connection_nodelay),
1956                           conf_checkbox_handler,
1957                           I(CONF_tcp_nodelay));
1958             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1959                           'p', HELPCTX(connection_tcpkeepalive),
1960                           conf_checkbox_handler,
1961                           I(CONF_tcp_keepalives));
1962 #ifndef NO_IPV6
1963             s = ctrl_getset(b, "Connection", "ipversion",
1964                           "Internet protocol version");
1965             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1966                           HELPCTX(connection_ipversion),
1967                           conf_radiobutton_handler,
1968                           I(CONF_addressfamily),
1969                           "Auto", 'u', I(ADDRTYPE_UNSPEC),
1970                           "IPv4", '4', I(ADDRTYPE_IPV4),
1971                           "IPv6", '6', I(ADDRTYPE_IPV6),
1972                           NULL);
1973 #endif
1974
1975             {
1976                 const char *label = backend_from_proto(PROT_SSH) ?
1977                     "Logical name of remote host (e.g. for SSH key lookup):" :
1978                     "Logical name of remote host:";
1979                 s = ctrl_getset(b, "Connection", "identity",
1980                                 "Logical name of remote host");
1981                 ctrl_editbox(s, label, 'm', 100,
1982                              HELPCTX(connection_loghost),
1983                              conf_editbox_handler, I(CONF_loghost), I(1));
1984             }
1985         }
1986
1987         /*
1988          * A sub-panel Connection/Data, containing options that
1989          * decide on data to send to the server.
1990          */
1991         if (!midsession) {
1992             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1993
1994             s = ctrl_getset(b, "Connection/Data", "login",
1995                             "Login details");
1996             ctrl_editbox(s, "Auto-login username", 'u', 50,
1997                          HELPCTX(connection_username),
1998                          conf_editbox_handler, I(CONF_username), I(1));
1999             {
2000                 /* We assume the local username is sufficiently stable
2001                  * to include on the dialog box. */
2002                 char *user = get_username();
2003                 char *userlabel = dupprintf("Use system username (%s)",
2004                                             user ? user : "");
2005                 sfree(user);
2006                 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
2007                                   HELPCTX(connection_username_from_env),
2008                                   conf_radiobutton_handler,
2009                                   I(CONF_username_from_env),
2010                                   "Prompt", I(FALSE),
2011                                   userlabel, I(TRUE),
2012                                   NULL);
2013                 sfree(userlabel);
2014             }
2015
2016             s = ctrl_getset(b, "Connection/Data", "term",
2017                             "Terminal details");
2018             ctrl_editbox(s, "Terminal-type string", 't', 50,
2019                          HELPCTX(connection_termtype),
2020                          conf_editbox_handler, I(CONF_termtype), I(1));
2021             ctrl_editbox(s, "Terminal speeds", 's', 50,
2022                          HELPCTX(connection_termspeed),
2023                          conf_editbox_handler, I(CONF_termspeed), I(1));
2024
2025             s = ctrl_getset(b, "Connection/Data", "env",
2026                             "Environment variables");
2027             ctrl_columns(s, 2, 80, 20);
2028             ed = (struct environ_data *)
2029                 ctrl_alloc(b, sizeof(struct environ_data));
2030             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
2031                                       HELPCTX(telnet_environ),
2032                                       environ_handler, P(ed), P(NULL));
2033             ed->varbox->generic.column = 0;
2034             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
2035                                       HELPCTX(telnet_environ),
2036                                       environ_handler, P(ed), P(NULL));
2037             ed->valbox->generic.column = 0;
2038             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
2039                                             HELPCTX(telnet_environ),
2040                                             environ_handler, P(ed));
2041             ed->addbutton->generic.column = 1;
2042             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2043                                             HELPCTX(telnet_environ),
2044                                             environ_handler, P(ed));
2045             ed->rembutton->generic.column = 1;
2046             ctrl_columns(s, 1, 100);
2047             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2048                                        HELPCTX(telnet_environ),
2049                                        environ_handler, P(ed));
2050             ed->listbox->listbox.height = 3;
2051             ed->listbox->listbox.ncols = 2;
2052             ed->listbox->listbox.percentages = snewn(2, int);
2053             ed->listbox->listbox.percentages[0] = 30;
2054             ed->listbox->listbox.percentages[1] = 70;
2055         }
2056
2057     }
2058
2059     if (!midsession) {
2060         /*
2061          * The Connection/Proxy panel.
2062          */
2063         ctrl_settitle(b, "Connection/Proxy",
2064                       "Options controlling proxy usage");
2065
2066         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
2067         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
2068                           HELPCTX(proxy_type),
2069                           conf_radiobutton_handler,
2070                           I(CONF_proxy_type),
2071                           "None", I(PROXY_NONE),
2072                           "SOCKS 4", I(PROXY_SOCKS4),
2073                           "SOCKS 5", I(PROXY_SOCKS5),
2074                           "HTTP", I(PROXY_HTTP),
2075                           "Telnet", I(PROXY_TELNET),
2076                           NULL);
2077         ctrl_columns(s, 2, 80, 20);
2078         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
2079                          HELPCTX(proxy_main),
2080                          conf_editbox_handler,
2081                          I(CONF_proxy_host), I(1));
2082         c->generic.column = 0;
2083         c = ctrl_editbox(s, "Port", 'p', 100,
2084                          HELPCTX(proxy_main),
2085                          conf_editbox_handler,
2086                          I(CONF_proxy_port),
2087                          I(-1));
2088         c->generic.column = 1;
2089         ctrl_columns(s, 1, 100);
2090         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
2091                      HELPCTX(proxy_exclude),
2092                      conf_editbox_handler,
2093                      I(CONF_proxy_exclude_list), I(1));
2094         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
2095                       HELPCTX(proxy_exclude),
2096                       conf_checkbox_handler,
2097                       I(CONF_even_proxy_localhost));
2098         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
2099                           HELPCTX(proxy_dns),
2100                           conf_radiobutton_handler,
2101                           I(CONF_proxy_dns),
2102                           "No", I(FORCE_OFF),
2103                           "Auto", I(AUTO),
2104                           "Yes", I(FORCE_ON), NULL);
2105         ctrl_editbox(s, "Username", 'u', 60,
2106                      HELPCTX(proxy_auth),
2107                      conf_editbox_handler,
2108                      I(CONF_proxy_username), I(1));
2109         c = ctrl_editbox(s, "Password", 'w', 60,
2110                          HELPCTX(proxy_auth),
2111                          conf_editbox_handler,
2112                          I(CONF_proxy_password), I(1));
2113         c->editbox.password = 1;
2114         ctrl_editbox(s, "Telnet command", 'm', 100,
2115                      HELPCTX(proxy_command),
2116                      conf_editbox_handler,
2117                      I(CONF_proxy_telnet_command), I(1));
2118
2119         ctrl_radiobuttons(s, "Print proxy diagnostics "
2120                           "in the terminal window", 'r', 5,
2121                           HELPCTX(proxy_main),
2122                           conf_radiobutton_handler,
2123                           I(CONF_proxy_log_to_term),
2124                           "No", I(FORCE_OFF),
2125                           "Yes", I(FORCE_ON),
2126                           "Only until session starts", I(AUTO), NULL);
2127     }
2128
2129     /*
2130      * The Telnet panel exists in the base config box, and in a
2131      * mid-session reconfig box _if_ we're using Telnet.
2132      */
2133     if (!midsession || protocol == PROT_TELNET) {
2134         /*
2135          * The Connection/Telnet panel.
2136          */
2137         ctrl_settitle(b, "Connection/Telnet",
2138                       "Options controlling Telnet connections");
2139
2140         s = ctrl_getset(b, "Connection/Telnet", "protocol",
2141                         "Telnet protocol adjustments");
2142
2143         if (!midsession) {
2144             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2145                               NO_SHORTCUT, 2,
2146                               HELPCTX(telnet_oldenviron),
2147                               conf_radiobutton_handler,
2148                               I(CONF_rfc_environ),
2149                               "BSD (commonplace)", 'b', I(0),
2150                               "RFC 1408 (unusual)", 'f', I(1), NULL);
2151             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2152                               HELPCTX(telnet_passive),
2153                               conf_radiobutton_handler,
2154                               I(CONF_passive_telnet),
2155                               "Passive", I(1), "Active", I(0), NULL);
2156         }
2157         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2158                       HELPCTX(telnet_specialkeys),
2159                       conf_checkbox_handler,
2160                       I(CONF_telnet_keyboard));
2161         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2162                       'm', HELPCTX(telnet_newline),
2163                       conf_checkbox_handler,
2164                       I(CONF_telnet_newline));
2165     }
2166
2167     if (!midsession) {
2168
2169         /*
2170          * The Connection/Rlogin panel.
2171          */
2172         ctrl_settitle(b, "Connection/Rlogin",
2173                       "Options controlling Rlogin connections");
2174
2175         s = ctrl_getset(b, "Connection/Rlogin", "data",
2176                         "Data to send to the server");
2177         ctrl_editbox(s, "Local username:", 'l', 50,
2178                      HELPCTX(rlogin_localuser),
2179                      conf_editbox_handler, I(CONF_localusername), I(1));
2180
2181     }
2182
2183     /*
2184      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2185      * when we're not doing SSH.
2186      */
2187
2188     if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2189
2190         /*
2191          * The Connection/SSH panel.
2192          */
2193         ctrl_settitle(b, "Connection/SSH",
2194                       "Options controlling SSH connections");
2195
2196         /* SSH-1 or connection-sharing downstream */
2197         if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
2198             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2199             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2200                       "session; it is only here so that sub-panels of it can "
2201                       "exist without looking strange.", HELPCTX(no_help));
2202         }
2203
2204         if (!midsession) {
2205
2206             s = ctrl_getset(b, "Connection/SSH", "data",
2207                             "Data to send to the server");
2208             ctrl_editbox(s, "Remote command:", 'r', 100,
2209                          HELPCTX(ssh_command),
2210                          conf_editbox_handler, I(CONF_remote_cmd), I(1));
2211
2212             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2213             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2214                           HELPCTX(ssh_noshell),
2215                           conf_checkbox_handler,
2216                           I(CONF_ssh_no_shell));
2217         }
2218
2219         if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2220             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2221
2222             ctrl_checkbox(s, "Enable compression", 'e',
2223                           HELPCTX(ssh_compress),
2224                           conf_checkbox_handler,
2225                           I(CONF_compression));
2226         }
2227
2228         if (!midsession) {
2229             s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
2230
2231             ctrl_checkbox(s, "Share SSH connections if possible", 's',
2232                           HELPCTX(ssh_share),
2233                           conf_checkbox_handler,
2234                           I(CONF_ssh_connection_sharing));
2235
2236             ctrl_text(s, "Permitted roles in a shared connection:",
2237                       HELPCTX(ssh_share));
2238             ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
2239                           HELPCTX(ssh_share),
2240                           conf_checkbox_handler,
2241                           I(CONF_ssh_connection_sharing_upstream));
2242             ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
2243                           HELPCTX(ssh_share),
2244                           conf_checkbox_handler,
2245                           I(CONF_ssh_connection_sharing_downstream));
2246         }
2247
2248         if (!midsession) {
2249             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2250
2251             ctrl_radiobuttons(s, "SSH protocol version:", NO_SHORTCUT, 2,
2252                               HELPCTX(ssh_protocol),
2253                               conf_radiobutton_handler,
2254                               I(CONF_sshprot),
2255                               "2", '2', I(3),
2256                               "1 (INSECURE)", '1', I(0), NULL);
2257         }
2258
2259         /*
2260          * The Connection/SSH/Kex panel. (Owing to repeat key
2261          * exchange, much of this is meaningful in mid-session _if_
2262          * we're using SSH-2 and are not a connection-sharing
2263          * downstream, or haven't decided yet.)
2264          */
2265         if (protcfginfo != 1 && protcfginfo != -1) {
2266             ctrl_settitle(b, "Connection/SSH/Kex",
2267                           "Options controlling SSH key exchange");
2268
2269             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2270                             "Key exchange algorithm options");
2271             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2272                               HELPCTX(ssh_kexlist),
2273                               kexlist_handler, P(NULL));
2274             c->listbox.height = 5;
2275
2276             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2277                             "Options controlling key re-exchange");
2278
2279             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2280                          HELPCTX(ssh_kex_repeat),
2281                          conf_editbox_handler,
2282                          I(CONF_ssh_rekey_time),
2283                          I(-1));
2284             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2285                          HELPCTX(ssh_kex_repeat),
2286                          conf_editbox_handler,
2287                          I(CONF_ssh_rekey_data),
2288                          I(16));
2289             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2290                       HELPCTX(ssh_kex_repeat));
2291         }
2292
2293         /*
2294          * The 'Connection/SSH/Host keys' panel.
2295          */
2296         if (protcfginfo != 1 && protcfginfo != -1) {
2297             ctrl_settitle(b, "Connection/SSH/Host keys",
2298                           "Options controlling SSH host keys");
2299
2300             s = ctrl_getset(b, "Connection/SSH/Host keys", "main",
2301                             "Host key algorithm preference");
2302             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2303                               HELPCTX(ssh_hklist),
2304                               hklist_handler, P(NULL));
2305             c->listbox.height = 5;
2306         }
2307
2308         /*
2309          * Manual host key configuration is irrelevant mid-session,
2310          * as we enforce that the host key for rekeys is the
2311          * same as that used at the start of the session.
2312          */
2313         if (!midsession) {
2314             s = ctrl_getset(b, "Connection/SSH/Host keys", "hostkeys",
2315                             "Manually configure host keys for this connection");
2316
2317             ctrl_columns(s, 2, 75, 25);
2318             c = ctrl_text(s, "Host keys or fingerprints to accept:",
2319                           HELPCTX(ssh_kex_manual_hostkeys));
2320             c->generic.column = 0;
2321             /* You want to select from the list, _then_ hit Remove. So
2322              * tab order should be that way round. */
2323             mh = (struct manual_hostkey_data *)
2324                 ctrl_alloc(b,sizeof(struct manual_hostkey_data));
2325             mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2326                                             HELPCTX(ssh_kex_manual_hostkeys),
2327                                             manual_hostkey_handler, P(mh));
2328             mh->rembutton->generic.column = 1;
2329             mh->rembutton->generic.tabdelay = 1;
2330             mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2331                                        HELPCTX(ssh_kex_manual_hostkeys),
2332                                        manual_hostkey_handler, P(mh));
2333             /* This list box can't be very tall, because there's not
2334              * much room in the pane on Windows at least. This makes
2335              * it become really unhelpful if a horizontal scrollbar
2336              * appears, so we suppress that. */
2337             mh->listbox->listbox.height = 2;
2338             mh->listbox->listbox.hscroll = FALSE;
2339             ctrl_tabdelay(s, mh->rembutton);
2340             mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
2341                                       HELPCTX(ssh_kex_manual_hostkeys),
2342                                       manual_hostkey_handler, P(mh), P(NULL));
2343             mh->keybox->generic.column = 0;
2344             mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
2345                                             HELPCTX(ssh_kex_manual_hostkeys),
2346                                             manual_hostkey_handler, P(mh));
2347             mh->addbutton->generic.column = 1;
2348             ctrl_columns(s, 1, 100);
2349         }
2350
2351         if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2352             /*
2353              * The Connection/SSH/Cipher panel.
2354              */
2355             ctrl_settitle(b, "Connection/SSH/Cipher",
2356                           "Options controlling SSH encryption");
2357
2358             s = ctrl_getset(b, "Connection/SSH/Cipher",
2359                             "encryption", "Encryption options");
2360             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2361                               HELPCTX(ssh_ciphers),
2362                               cipherlist_handler, P(NULL));
2363             c->listbox.height = 6;
2364
2365             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2366                           HELPCTX(ssh_ciphers),
2367                           conf_checkbox_handler,
2368                           I(CONF_ssh2_des_cbc));
2369         }
2370
2371         if (!midsession) {
2372
2373             /*
2374              * The Connection/SSH/Auth panel.
2375              */
2376             ctrl_settitle(b, "Connection/SSH/Auth",
2377                           "Options controlling SSH authentication");
2378
2379             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2380             ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2381                           'd', HELPCTX(ssh_auth_banner),
2382                           conf_checkbox_handler,
2383                           I(CONF_ssh_show_banner));
2384             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2385                           HELPCTX(ssh_auth_bypass),
2386                           conf_checkbox_handler,
2387                           I(CONF_ssh_no_userauth));
2388
2389             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2390                             "Authentication methods");
2391             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2392                           HELPCTX(ssh_auth_pageant),
2393                           conf_checkbox_handler,
2394                           I(CONF_tryagent));
2395             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2396                           HELPCTX(ssh_auth_tis),
2397                           conf_checkbox_handler,
2398                           I(CONF_try_tis_auth));
2399             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2400                           'i', HELPCTX(ssh_auth_ki),
2401                           conf_checkbox_handler,
2402                           I(CONF_try_ki_auth));
2403
2404             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2405                             "Authentication parameters");
2406             ctrl_checkbox(s, "Allow agent forwarding", 'f',
2407                           HELPCTX(ssh_auth_agentfwd),
2408                           conf_checkbox_handler, I(CONF_agentfwd));
2409             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2410                           HELPCTX(ssh_auth_changeuser),
2411                           conf_checkbox_handler,
2412                           I(CONF_change_username));
2413             ctrl_filesel(s, "Private key file for authentication:", 'k',
2414                          FILTER_KEY_FILES, FALSE, "Select private key file",
2415                          HELPCTX(ssh_auth_privkey),
2416                          conf_filesel_handler, I(CONF_keyfile));
2417
2418 #ifndef NO_GSSAPI
2419             /*
2420              * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2421              * the main Auth panel.
2422              */
2423             ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2424                           "Options controlling GSSAPI authentication");
2425             s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2426
2427             ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2428                           't', HELPCTX(ssh_gssapi),
2429                           conf_checkbox_handler,
2430                           I(CONF_try_gssapi_auth));
2431
2432             ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2433                           HELPCTX(ssh_gssapi_delegation),
2434                           conf_checkbox_handler,
2435                           I(CONF_gssapifwd));
2436
2437             /*
2438              * GSSAPI library selection.
2439              */
2440             if (ngsslibs > 1) {
2441                 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2442                                   'p', HELPCTX(ssh_gssapi_libraries),
2443                                   gsslist_handler, P(NULL));
2444                 c->listbox.height = ngsslibs;
2445
2446                 /*
2447                  * I currently assume that if more than one GSS
2448                  * library option is available, then one of them is
2449                  * 'user-supplied' and so we should present the
2450                  * following file selector. This is at least half-
2451                  * reasonable, because if we're using statically
2452                  * linked GSSAPI then there will only be one option
2453                  * and no way to load from a user-supplied library,
2454                  * whereas if we're using dynamic libraries then
2455                  * there will almost certainly be some default
2456                  * option in addition to a user-supplied path. If
2457                  * anyone ever ports PuTTY to a system on which
2458                  * dynamic-library GSSAPI is available but there is
2459                  * absolutely no consensus on where to keep the
2460                  * libraries, there'll need to be a flag alongside
2461                  * ngsslibs to control whether the file selector is
2462                  * displayed. 
2463                  */
2464
2465                 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2466                              FILTER_DYNLIB_FILES, FALSE, "Select library file",
2467                              HELPCTX(ssh_gssapi_libraries),
2468                              conf_filesel_handler,
2469                              I(CONF_ssh_gss_custom));
2470             }
2471 #endif
2472         }
2473
2474         if (!midsession) {
2475             /*
2476              * The Connection/SSH/TTY panel.
2477              */
2478             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2479
2480             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2481             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2482                           HELPCTX(ssh_nopty),
2483                           conf_checkbox_handler,
2484                           I(CONF_nopty));
2485
2486             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2487                             "Terminal modes");
2488             td = (struct ttymodes_data *)
2489                 ctrl_alloc(b, sizeof(struct ttymodes_data));
2490             ctrl_columns(s, 2, 75, 25);
2491             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2492             c->generic.column = 0;
2493             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2494                                             HELPCTX(ssh_ttymodes),
2495                                             ttymodes_handler, P(td));
2496             td->rembutton->generic.column = 1;
2497             td->rembutton->generic.tabdelay = 1;
2498             ctrl_columns(s, 1, 100);
2499             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2500                                        HELPCTX(ssh_ttymodes),
2501                                        ttymodes_handler, P(td));
2502             td->listbox->listbox.multisel = 1;
2503             td->listbox->listbox.height = 4;
2504             td->listbox->listbox.ncols = 2;
2505             td->listbox->listbox.percentages = snewn(2, int);
2506             td->listbox->listbox.percentages[0] = 40;
2507             td->listbox->listbox.percentages[1] = 60;
2508             ctrl_tabdelay(s, td->rembutton);
2509             ctrl_columns(s, 2, 75, 25);
2510             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2511                                          HELPCTX(ssh_ttymodes),
2512                                          ttymodes_handler, P(td));
2513             td->modelist->generic.column = 0;
2514             td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2515                                             HELPCTX(ssh_ttymodes),
2516                                             ttymodes_handler, P(td));
2517             td->addbutton->generic.column = 1;
2518             td->addbutton->generic.tabdelay = 1;
2519             ctrl_columns(s, 1, 100);        /* column break */
2520             /* Bit of a hack to get the value radio buttons and
2521              * edit-box on the same row. */
2522             ctrl_columns(s, 3, 25, 50, 25);
2523             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2524             c->generic.column = 0;
2525             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2526                                              HELPCTX(ssh_ttymodes),
2527                                              ttymodes_handler, P(td),
2528                                              "Auto", NO_SHORTCUT, P(NULL),
2529                                              "This:", NO_SHORTCUT, P(NULL),
2530                                              NULL);
2531             td->valradio->generic.column = 1;
2532             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2533                                       HELPCTX(ssh_ttymodes),
2534                                       ttymodes_handler, P(td), P(NULL));
2535             td->valbox->generic.column = 2;
2536             ctrl_tabdelay(s, td->addbutton);
2537
2538         }
2539
2540         if (!midsession) {
2541             /*
2542              * The Connection/SSH/X11 panel.
2543              */
2544             ctrl_settitle(b, "Connection/SSH/X11",
2545                           "Options controlling SSH X11 forwarding");
2546
2547             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2548             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2549                           HELPCTX(ssh_tunnels_x11),
2550                           conf_checkbox_handler,I(CONF_x11_forward));
2551             ctrl_editbox(s, "X display location", 'x', 50,
2552                          HELPCTX(ssh_tunnels_x11),
2553                          conf_editbox_handler, I(CONF_x11_display), I(1));
2554             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2555                               HELPCTX(ssh_tunnels_x11auth),
2556                               conf_radiobutton_handler,
2557                               I(CONF_x11_auth),
2558                               "MIT-Magic-Cookie-1", I(X11_MIT),
2559                               "XDM-Authorization-1", I(X11_XDM), NULL);
2560         }
2561
2562         /*
2563          * The Tunnels panel _is_ still available in mid-session.
2564          */
2565         ctrl_settitle(b, "Connection/SSH/Tunnels",
2566                       "Options controlling SSH port forwarding");
2567
2568         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2569                         "Port forwarding");
2570         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2571                       HELPCTX(ssh_tunnels_portfwd_localhost),
2572                       conf_checkbox_handler,
2573                       I(CONF_lport_acceptall));
2574         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2575                       HELPCTX(ssh_tunnels_portfwd_localhost),
2576                       conf_checkbox_handler,
2577                       I(CONF_rport_acceptall));
2578
2579         ctrl_columns(s, 3, 55, 20, 25);
2580         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2581         c->generic.column = COLUMN_FIELD(0,2);
2582         /* You want to select from the list, _then_ hit Remove. So tab order
2583          * should be that way round. */
2584         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2585         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2586                                          HELPCTX(ssh_tunnels_portfwd),
2587                                          portfwd_handler, P(pfd));
2588         pfd->rembutton->generic.column = 2;
2589         pfd->rembutton->generic.tabdelay = 1;
2590         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2591                                     HELPCTX(ssh_tunnels_portfwd),
2592                                     portfwd_handler, P(pfd));
2593         pfd->listbox->listbox.height = 3;
2594         pfd->listbox->listbox.ncols = 2;
2595         pfd->listbox->listbox.percentages = snewn(2, int);
2596         pfd->listbox->listbox.percentages[0] = 20;
2597         pfd->listbox->listbox.percentages[1] = 80;
2598         ctrl_tabdelay(s, pfd->rembutton);
2599         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2600         /* You want to enter source, destination and type, _then_ hit Add.
2601          * Again, we adjust the tab order to reflect this. */
2602         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2603                                          HELPCTX(ssh_tunnels_portfwd),
2604                                          portfwd_handler, P(pfd));
2605         pfd->addbutton->generic.column = 2;
2606         pfd->addbutton->generic.tabdelay = 1;
2607         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2608                                       HELPCTX(ssh_tunnels_portfwd),
2609                                       portfwd_handler, P(pfd), P(NULL));
2610         pfd->sourcebox->generic.column = 0;
2611         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2612                                     HELPCTX(ssh_tunnels_portfwd),
2613                                     portfwd_handler, P(pfd), P(NULL));
2614         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2615                                            HELPCTX(ssh_tunnels_portfwd),
2616                                            portfwd_handler, P(pfd),
2617                                            "Local", 'l', P(NULL),
2618                                            "Remote", 'm', P(NULL),
2619                                            "Dynamic", 'y', P(NULL),
2620                                            NULL);
2621 #ifndef NO_IPV6
2622         pfd->addressfamily =
2623             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2624                               HELPCTX(ssh_tunnels_portfwd_ipversion),
2625                               portfwd_handler, P(pfd),
2626                               "Auto", 'u', I(ADDRTYPE_UNSPEC),
2627                               "IPv4", '4', I(ADDRTYPE_IPV4),
2628                               "IPv6", '6', I(ADDRTYPE_IPV6),
2629                               NULL);
2630 #endif
2631         ctrl_tabdelay(s, pfd->addbutton);
2632         ctrl_columns(s, 1, 100);
2633
2634         if (!midsession) {
2635             /*
2636              * The Connection/SSH/Bugs panels.
2637              */
2638             ctrl_settitle(b, "Connection/SSH/Bugs",
2639                           "Workarounds for SSH server bugs");
2640
2641             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2642                             "Detection of known bugs in SSH servers");
2643             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2644                           HELPCTX(ssh_bugs_ignore1),
2645                           sshbug_handler, I(CONF_sshbug_ignore1));
2646             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2647                           HELPCTX(ssh_bugs_plainpw1),
2648                           sshbug_handler, I(CONF_sshbug_plainpw1));
2649             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2650                           HELPCTX(ssh_bugs_rsa1),
2651                           sshbug_handler, I(CONF_sshbug_rsa1));
2652             ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2653                           HELPCTX(ssh_bugs_ignore2),
2654                           sshbug_handler, I(CONF_sshbug_ignore2));
2655             ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
2656                           20, HELPCTX(ssh_bugs_winadj),
2657                           sshbug_handler, I(CONF_sshbug_winadj));
2658             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2659                           HELPCTX(ssh_bugs_hmac2),
2660                           sshbug_handler, I(CONF_sshbug_hmac2));
2661             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2662                           HELPCTX(ssh_bugs_derivekey2),
2663                           sshbug_handler, I(CONF_sshbug_derivekey2));
2664
2665             ctrl_settitle(b, "Connection/SSH/More bugs",
2666                           "Further workarounds for SSH server bugs");
2667
2668             s = ctrl_getset(b, "Connection/SSH/More bugs", "main",
2669                             "Detection of known bugs in SSH servers");
2670             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2671                           HELPCTX(ssh_bugs_rsapad2),
2672                           sshbug_handler, I(CONF_sshbug_rsapad2));
2673             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2674                           HELPCTX(ssh_bugs_pksessid2),
2675                           sshbug_handler, I(CONF_sshbug_pksessid2));
2676             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2677                           HELPCTX(ssh_bugs_rekey2),
2678                           sshbug_handler, I(CONF_sshbug_rekey2));
2679             ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2680                           HELPCTX(ssh_bugs_maxpkt2),
2681                           sshbug_handler, I(CONF_sshbug_maxpkt2));
2682             ctrl_droplist(s, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20,
2683                           HELPCTX(ssh_bugs_oldgex2),
2684                           sshbug_handler, I(CONF_sshbug_oldgex2));
2685             ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
2686                           HELPCTX(ssh_bugs_chanreq),
2687                           sshbug_handler, I(CONF_sshbug_chanreq));
2688         }
2689     }
2690 }