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