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