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