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