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