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