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