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