]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Introduce a third setting for the 'bold as colour' mode, which lets
[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 i, whichbutton;
1132
1133             i = 0;
1134 #ifndef NO_IPV6
1135             whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1136             if (whichbutton == 1)
1137                 family = "4";
1138             else if (whichbutton == 2)
1139                 family = "6";
1140             else
1141                 family = "";
1142 #endif
1143
1144             whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1145             if (whichbutton == 0)
1146                 type = "L";
1147             else if (whichbutton == 1)
1148                 type = "R";
1149             else
1150                 type = "D";
1151
1152             src = dlg_editbox_get(pfd->sourcebox, dlg);
1153             if (!*src) {
1154                 dlg_error_msg(dlg, "You need to specify a source port number");
1155                 sfree(src);
1156                 return;
1157             }
1158             if (*type != 'D') {
1159                 val = dlg_editbox_get(pfd->destbox, dlg);
1160                 if (!*val || !strchr(val, ':')) {
1161                     dlg_error_msg(dlg,
1162                                   "You need to specify a destination address\n"
1163                                   "in the form \"host.name:port\"");
1164                     sfree(src);
1165                     sfree(val);
1166                     return;
1167                 }
1168             } else {
1169                 type = "L";
1170                 val = dupstr("D");     /* special case */
1171             }
1172
1173             key = dupcat(family, type, src, NULL);
1174             sfree(src);
1175
1176             if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1177                 dlg_error_msg(dlg, "Specified forwarding already exists");
1178             } else {
1179                 conf_set_str_str(conf, CONF_portfwd, key, val);
1180             }
1181
1182             sfree(key);
1183             sfree(val);
1184             dlg_refresh(pfd->listbox, dlg);
1185         } else if (ctrl == pfd->rembutton) {
1186             int i = dlg_listbox_index(pfd->listbox, dlg);
1187             if (i < 0) {
1188                 dlg_beep(dlg);
1189             } else {
1190                 char *key, *val, *p;
1191
1192                 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1193                 if (key) {
1194                     static const char *const afs = "A46";
1195                     static const char *const dirs = "LRD";
1196                     char *afp;
1197                     int dir;
1198 #ifndef NO_IPV6
1199                     int idx;
1200 #endif
1201
1202                     /* Populate controls with the entry we're about to delete
1203                      * for ease of editing */
1204                     p = key;
1205
1206                     afp = strchr(afs, *p);
1207 #ifndef NO_IPV6
1208                     idx = afp ? afp-afs : 0;
1209 #endif
1210                     if (afp)
1211                         p++;
1212 #ifndef NO_IPV6
1213                     dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1214 #endif
1215
1216                     dir = *p;
1217
1218                     val = conf_get_str_str(conf, CONF_portfwd, key);
1219                     if (!strcmp(val, "D")) {
1220                         dir = 'D';
1221                         val = "";
1222                     }
1223
1224                     dlg_radiobutton_set(pfd->direction, dlg,
1225                                         strchr(dirs, dir) - dirs);
1226                     p++;
1227
1228                     dlg_editbox_set(pfd->sourcebox, dlg, p);
1229                     dlg_editbox_set(pfd->destbox, dlg, val);
1230                     /* And delete it */
1231                     conf_del_str_str(conf, CONF_portfwd, key);
1232                 }
1233             }
1234             dlg_refresh(pfd->listbox, dlg);
1235         }
1236     }
1237 }
1238
1239 void setup_config_box(struct controlbox *b, int midsession,
1240                       int protocol, int protcfginfo)
1241 {
1242     struct controlset *s;
1243     struct sessionsaver_data *ssd;
1244     struct charclass_data *ccd;
1245     struct colour_data *cd;
1246     struct ttymodes_data *td;
1247     struct environ_data *ed;
1248     struct portfwd_data *pfd;
1249     union control *c;
1250     char *str;
1251
1252     ssd = (struct sessionsaver_data *)
1253         ctrl_alloc(b, sizeof(struct sessionsaver_data));
1254     memset(ssd, 0, sizeof(*ssd));
1255     ssd->midsession = midsession;
1256
1257     /*
1258      * The standard panel that appears at the bottom of all panels:
1259      * Open, Cancel, Apply etc.
1260      */
1261     s = ctrl_getset(b, "", "", "");
1262     ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1263     ssd->okbutton = ctrl_pushbutton(s,
1264                                     (midsession ? "Apply" : "Open"),
1265                                     (char)(midsession ? 'a' : 'o'),
1266                                     HELPCTX(no_help),
1267                                     sessionsaver_handler, P(ssd));
1268     ssd->okbutton->button.isdefault = TRUE;
1269     ssd->okbutton->generic.column = 3;
1270     ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1271                                         sessionsaver_handler, P(ssd));
1272     ssd->cancelbutton->button.iscancel = TRUE;
1273     ssd->cancelbutton->generic.column = 4;
1274     /* We carefully don't close the 5-column part, so that platform-
1275      * specific add-ons can put extra buttons alongside Open and Cancel. */
1276
1277     /*
1278      * The Session panel.
1279      */
1280     str = dupprintf("Basic options for your %s session", appname);
1281     ctrl_settitle(b, "Session", str);
1282     sfree(str);
1283
1284     if (!midsession) {
1285         struct hostport *hp = (struct hostport *)
1286             ctrl_alloc(b, sizeof(struct hostport));
1287
1288         s = ctrl_getset(b, "Session", "hostport",
1289                         "Specify the destination you want to connect to");
1290         ctrl_columns(s, 2, 75, 25);
1291         c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1292                          HELPCTX(session_hostname),
1293                          config_host_handler, I(0), I(0));
1294         c->generic.column = 0;
1295         hp->host = c;
1296         c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1297                          HELPCTX(session_hostname),
1298                          config_port_handler, I(0), I(0));
1299         c->generic.column = 1;
1300         hp->port = c;
1301         ctrl_columns(s, 1, 100);
1302
1303         if (!backend_from_proto(PROT_SSH)) {
1304             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1305                               HELPCTX(session_hostname),
1306                               config_protocolbuttons_handler, P(hp),
1307                               "Raw", 'w', I(PROT_RAW),
1308                               "Telnet", 't', I(PROT_TELNET),
1309                               "Rlogin", 'i', I(PROT_RLOGIN),
1310                               NULL);
1311         } else {
1312             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1313                               HELPCTX(session_hostname),
1314                               config_protocolbuttons_handler, P(hp),
1315                               "Raw", 'w', I(PROT_RAW),
1316                               "Telnet", 't', I(PROT_TELNET),
1317                               "Rlogin", 'i', I(PROT_RLOGIN),
1318                               "SSH", 's', I(PROT_SSH),
1319                               NULL);
1320         }
1321     }
1322
1323     /*
1324      * The Load/Save panel is available even in mid-session.
1325      */
1326     s = ctrl_getset(b, "Session", "savedsessions",
1327                     midsession ? "Save the current session settings" :
1328                     "Load, save or delete a stored session");
1329     ctrl_columns(s, 2, 75, 25);
1330     get_sesslist(&ssd->sesslist, TRUE);
1331     ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1332                                 HELPCTX(session_saved),
1333                                 sessionsaver_handler, P(ssd), P(NULL));
1334     ssd->editbox->generic.column = 0;
1335     /* Reset columns so that the buttons are alongside the list, rather
1336      * than alongside that edit box. */
1337     ctrl_columns(s, 1, 100);
1338     ctrl_columns(s, 2, 75, 25);
1339     ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1340                                 HELPCTX(session_saved),
1341                                 sessionsaver_handler, P(ssd));
1342     ssd->listbox->generic.column = 0;
1343     ssd->listbox->listbox.height = 7;
1344     if (!midsession) {
1345         ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1346                                           HELPCTX(session_saved),
1347                                           sessionsaver_handler, P(ssd));
1348         ssd->loadbutton->generic.column = 1;
1349     } else {
1350         /* We can't offer the Load button mid-session, as it would allow the
1351          * user to load and subsequently save settings they can't see. (And
1352          * also change otherwise immutable settings underfoot; that probably
1353          * shouldn't be a problem, but.) */
1354         ssd->loadbutton = NULL;
1355     }
1356     /* "Save" button is permitted mid-session. */
1357     ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1358                                       HELPCTX(session_saved),
1359                                       sessionsaver_handler, P(ssd));
1360     ssd->savebutton->generic.column = 1;
1361     if (!midsession) {
1362         ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1363                                          HELPCTX(session_saved),
1364                                          sessionsaver_handler, P(ssd));
1365         ssd->delbutton->generic.column = 1;
1366     } else {
1367         /* Disable the Delete button mid-session too, for UI consistency. */
1368         ssd->delbutton = NULL;
1369     }
1370     ctrl_columns(s, 1, 100);
1371
1372     s = ctrl_getset(b, "Session", "otheropts", NULL);
1373     c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1374                           HELPCTX(session_coe),
1375                           conf_radiobutton_handler,
1376                           I(CONF_close_on_exit),
1377                           "Always", I(FORCE_ON),
1378                           "Never", I(FORCE_OFF),
1379                           "Only on clean exit", I(AUTO), NULL);
1380
1381     /*
1382      * The Session/Logging panel.
1383      */
1384     ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1385
1386     s = ctrl_getset(b, "Session/Logging", "main", NULL);
1387     /*
1388      * The logging buttons change depending on whether SSH packet
1389      * logging can sensibly be available.
1390      */
1391     {
1392         char *sshlogname, *sshrawlogname;
1393         if ((midsession && protocol == PROT_SSH) ||
1394             (!midsession && backend_from_proto(PROT_SSH))) {
1395             sshlogname = "SSH packets";
1396             sshrawlogname = "SSH packets and raw data";
1397         } else {
1398             sshlogname = NULL;         /* this will disable both buttons */
1399             sshrawlogname = NULL;      /* this will just placate optimisers */
1400         }
1401         ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1402                           HELPCTX(logging_main),
1403                           loggingbuttons_handler,
1404                           I(CONF_logtype),
1405                           "None", 't', I(LGTYP_NONE),
1406                           "Printable output", 'p', I(LGTYP_ASCII),
1407                           "All session output", 'l', I(LGTYP_DEBUG),
1408                           sshlogname, 's', I(LGTYP_PACKETS),
1409                           sshrawlogname, 'r', I(LGTYP_SSHRAW),
1410                           NULL);
1411     }
1412     ctrl_filesel(s, "Log file name:", 'f',
1413                  NULL, TRUE, "Select session log file name",
1414                  HELPCTX(logging_filename),
1415                  conf_filesel_handler, I(CONF_logfilename));
1416     ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1417               " &T for time, and &H for host name)",
1418               HELPCTX(logging_filename));
1419     ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1420                       HELPCTX(logging_exists),
1421                       conf_radiobutton_handler, I(CONF_logxfovr),
1422                       "Always overwrite it", I(LGXF_OVR),
1423                       "Always append to the end of it", I(LGXF_APN),
1424                       "Ask the user every time", I(LGXF_ASK), NULL);
1425     ctrl_checkbox(s, "Flush log file frequently", 'u',
1426                  HELPCTX(logging_flush),
1427                  conf_checkbox_handler, I(CONF_logflush));
1428
1429     if ((midsession && protocol == PROT_SSH) ||
1430         (!midsession && backend_from_proto(PROT_SSH))) {
1431         s = ctrl_getset(b, "Session/Logging", "ssh",
1432                         "Options specific to SSH packet logging");
1433         ctrl_checkbox(s, "Omit known password fields", 'k',
1434                       HELPCTX(logging_ssh_omit_password),
1435                       conf_checkbox_handler, I(CONF_logomitpass));
1436         ctrl_checkbox(s, "Omit session data", 'd',
1437                       HELPCTX(logging_ssh_omit_data),
1438                       conf_checkbox_handler, I(CONF_logomitdata));
1439     }
1440
1441     /*
1442      * The Terminal panel.
1443      */
1444     ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1445
1446     s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1447     ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1448                   HELPCTX(terminal_autowrap),
1449                   conf_checkbox_handler, I(CONF_wrap_mode));
1450     ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1451                   HELPCTX(terminal_decom),
1452                   conf_checkbox_handler, I(CONF_dec_om));
1453     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1454                   HELPCTX(terminal_lfhascr),
1455                   conf_checkbox_handler, I(CONF_lfhascr));
1456     ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1457                   HELPCTX(terminal_crhaslf),
1458                   conf_checkbox_handler, I(CONF_crhaslf));
1459     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1460                   HELPCTX(terminal_bce),
1461                   conf_checkbox_handler, I(CONF_bce));
1462     ctrl_checkbox(s, "Enable blinking text", 'n',
1463                   HELPCTX(terminal_blink),
1464                   conf_checkbox_handler, I(CONF_blinktext));
1465     ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1466                  HELPCTX(terminal_answerback),
1467                  conf_editbox_handler, I(CONF_answerback), I(1));
1468
1469     s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1470     ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1471                       HELPCTX(terminal_localecho),
1472                       conf_radiobutton_handler,I(CONF_localecho),
1473                       "Auto", I(AUTO),
1474                       "Force on", I(FORCE_ON),
1475                       "Force off", I(FORCE_OFF), NULL);
1476     ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1477                       HELPCTX(terminal_localedit),
1478                       conf_radiobutton_handler,I(CONF_localedit),
1479                       "Auto", I(AUTO),
1480                       "Force on", I(FORCE_ON),
1481                       "Force off", I(FORCE_OFF), NULL);
1482
1483     s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1484     ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1485                   HELPCTX(terminal_printing),
1486                   printerbox_handler, P(NULL), P(NULL));
1487
1488     /*
1489      * The Terminal/Keyboard panel.
1490      */
1491     ctrl_settitle(b, "Terminal/Keyboard",
1492                   "Options controlling the effects of keys");
1493
1494     s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1495                     "Change the sequences sent by:");
1496     ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1497                       HELPCTX(keyboard_backspace),
1498                       conf_radiobutton_handler,
1499                       I(CONF_bksp_is_delete),
1500                       "Control-H", I(0), "Control-? (127)", I(1), NULL);
1501     ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1502                       HELPCTX(keyboard_homeend),
1503                       conf_radiobutton_handler,
1504                       I(CONF_rxvt_homeend),
1505                       "Standard", I(0), "rxvt", I(1), NULL);
1506     ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1507                       HELPCTX(keyboard_funkeys),
1508                       conf_radiobutton_handler,
1509                       I(CONF_funky_type),
1510                       "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1511                       "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1512
1513     s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1514                     "Application keypad settings:");
1515     ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1516                       HELPCTX(keyboard_appcursor),
1517                       conf_radiobutton_handler,
1518                       I(CONF_app_cursor),
1519                       "Normal", I(0), "Application", I(1), NULL);
1520     ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1521                       HELPCTX(keyboard_appkeypad),
1522                       numeric_keypad_handler, P(NULL),
1523                       "Normal", I(0), "Application", I(1), "NetHack", I(2),
1524                       NULL);
1525
1526     /*
1527      * The Terminal/Bell panel.
1528      */
1529     ctrl_settitle(b, "Terminal/Bell",
1530                   "Options controlling the terminal bell");
1531
1532     s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1533     ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1534                       HELPCTX(bell_style),
1535                       conf_radiobutton_handler, I(CONF_beep),
1536                       "None (bell disabled)", I(BELL_DISABLED),
1537                       "Make default system alert sound", I(BELL_DEFAULT),
1538                       "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1539
1540     s = ctrl_getset(b, "Terminal/Bell", "overload",
1541                     "Control the bell overload behaviour");
1542     ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1543                   HELPCTX(bell_overload),
1544                   conf_checkbox_handler, I(CONF_bellovl));
1545     ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1546                  HELPCTX(bell_overload),
1547                  conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1548     ctrl_editbox(s, "... in this many seconds", 't', 20,
1549                  HELPCTX(bell_overload),
1550                  conf_editbox_handler, I(CONF_bellovl_t),
1551                  I(-TICKSPERSEC));
1552     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1553               HELPCTX(bell_overload));
1554     ctrl_editbox(s, "Seconds of silence required", 's', 20,
1555                  HELPCTX(bell_overload),
1556                  conf_editbox_handler, I(CONF_bellovl_s),
1557                  I(-TICKSPERSEC));
1558
1559     /*
1560      * The Terminal/Features panel.
1561      */
1562     ctrl_settitle(b, "Terminal/Features",
1563                   "Enabling and disabling advanced terminal features");
1564
1565     s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1566     ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1567                   HELPCTX(features_application),
1568                   conf_checkbox_handler, I(CONF_no_applic_c));
1569     ctrl_checkbox(s, "Disable application keypad mode", 'k',
1570                   HELPCTX(features_application),
1571                   conf_checkbox_handler, I(CONF_no_applic_k));
1572     ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1573                   HELPCTX(features_mouse),
1574                   conf_checkbox_handler, I(CONF_no_mouse_rep));
1575     ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1576                   HELPCTX(features_resize),
1577                   conf_checkbox_handler,
1578                   I(CONF_no_remote_resize));
1579     ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1580                   HELPCTX(features_altscreen),
1581                   conf_checkbox_handler, I(CONF_no_alt_screen));
1582     ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1583                   HELPCTX(features_retitle),
1584                   conf_checkbox_handler,
1585                   I(CONF_no_remote_wintitle));
1586     ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1587                       HELPCTX(features_qtitle),
1588                       conf_radiobutton_handler,
1589                       I(CONF_remote_qtitle_action),
1590                       "None", I(TITLE_NONE),
1591                       "Empty string", I(TITLE_EMPTY),
1592                       "Window title", I(TITLE_REAL), NULL);
1593     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1594                   HELPCTX(features_dbackspace),
1595                   conf_checkbox_handler, I(CONF_no_dbackspace));
1596     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1597                   'r', HELPCTX(features_charset), conf_checkbox_handler,
1598                   I(CONF_no_remote_charset));
1599     ctrl_checkbox(s, "Disable Arabic text shaping",
1600                   'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1601                   I(CONF_arabicshaping));
1602     ctrl_checkbox(s, "Disable bidirectional text display",
1603                   'd', HELPCTX(features_bidi), conf_checkbox_handler,
1604                   I(CONF_bidi));
1605
1606     /*
1607      * The Window panel.
1608      */
1609     str = dupprintf("Options controlling %s's window", appname);
1610     ctrl_settitle(b, "Window", str);
1611     sfree(str);
1612
1613     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1614     ctrl_columns(s, 2, 50, 50);
1615     c = ctrl_editbox(s, "Columns", 'm', 100,
1616                      HELPCTX(window_size),
1617                      conf_editbox_handler, I(CONF_width), I(-1));
1618     c->generic.column = 0;
1619     c = ctrl_editbox(s, "Rows", 'r', 100,
1620                      HELPCTX(window_size),
1621                      conf_editbox_handler, I(CONF_height),I(-1));
1622     c->generic.column = 1;
1623     ctrl_columns(s, 1, 100);
1624
1625     s = ctrl_getset(b, "Window", "scrollback",
1626                     "Control the scrollback in the window");
1627     ctrl_editbox(s, "Lines of scrollback", 's', 50,
1628                  HELPCTX(window_scrollback),
1629                  conf_editbox_handler, I(CONF_savelines), I(-1));
1630     ctrl_checkbox(s, "Display scrollbar", 'd',
1631                   HELPCTX(window_scrollback),
1632                   conf_checkbox_handler, I(CONF_scrollbar));
1633     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1634                   HELPCTX(window_scrollback),
1635                   conf_checkbox_handler, I(CONF_scroll_on_key));
1636     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1637                   HELPCTX(window_scrollback),
1638                   conf_checkbox_handler, I(CONF_scroll_on_disp));
1639     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1640                   HELPCTX(window_erased),
1641                   conf_checkbox_handler,
1642                   I(CONF_erase_to_scrollback));
1643
1644     /*
1645      * The Window/Appearance panel.
1646      */
1647     str = dupprintf("Configure the appearance of %s's window", appname);
1648     ctrl_settitle(b, "Window/Appearance", str);
1649     sfree(str);
1650
1651     s = ctrl_getset(b, "Window/Appearance", "cursor",
1652                     "Adjust the use of the cursor");
1653     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1654                       HELPCTX(appearance_cursor),
1655                       conf_radiobutton_handler,
1656                       I(CONF_cursor_type),
1657                       "Block", 'l', I(0),
1658                       "Underline", 'u', I(1),
1659                       "Vertical line", 'v', I(2), NULL);
1660     ctrl_checkbox(s, "Cursor blinks", 'b',
1661                   HELPCTX(appearance_cursor),
1662                   conf_checkbox_handler, I(CONF_blink_cur));
1663
1664     s = ctrl_getset(b, "Window/Appearance", "font",
1665                     "Font settings");
1666     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1667                  HELPCTX(appearance_font),
1668                  conf_fontsel_handler, I(CONF_font));
1669
1670     s = ctrl_getset(b, "Window/Appearance", "mouse",
1671                     "Adjust the use of the mouse pointer");
1672     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1673                   HELPCTX(appearance_hidemouse),
1674                   conf_checkbox_handler, I(CONF_hide_mouseptr));
1675
1676     s = ctrl_getset(b, "Window/Appearance", "border",
1677                     "Adjust the window border");
1678     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1679                  HELPCTX(appearance_border),
1680                  conf_editbox_handler,
1681                  I(CONF_window_border), I(-1));
1682
1683     /*
1684      * The Window/Behaviour panel.
1685      */
1686     str = dupprintf("Configure the behaviour of %s's window", appname);
1687     ctrl_settitle(b, "Window/Behaviour", str);
1688     sfree(str);
1689
1690     s = ctrl_getset(b, "Window/Behaviour", "title",
1691                     "Adjust the behaviour of the window title");
1692     ctrl_editbox(s, "Window title:", 't', 100,
1693                  HELPCTX(appearance_title),
1694                  conf_editbox_handler, I(CONF_wintitle), I(1));
1695     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1696                   HELPCTX(appearance_title),
1697                   conf_checkbox_handler,
1698                   I(CHECKBOX_INVERT | CONF_win_name_always));
1699
1700     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1701     ctrl_checkbox(s, "Warn before closing window", 'w',
1702                   HELPCTX(behaviour_closewarn),
1703                   conf_checkbox_handler, I(CONF_warn_on_close));
1704
1705     /*
1706      * The Window/Translation panel.
1707      */
1708     ctrl_settitle(b, "Window/Translation",
1709                   "Options controlling character set translation");
1710
1711     s = ctrl_getset(b, "Window/Translation", "trans",
1712                     "Character set translation");
1713     ctrl_combobox(s, "Remote character set:",
1714                   'r', 100, HELPCTX(translation_codepage),
1715                   codepage_handler, P(NULL), P(NULL));
1716
1717     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1718     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1719                   HELPCTX(translation_cjk_ambig_wide),
1720                   conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1721
1722     str = dupprintf("Adjust how %s handles line drawing characters", appname);
1723     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1724     sfree(str);
1725     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1726                       HELPCTX(translation_linedraw),
1727                       conf_radiobutton_handler,
1728                       I(CONF_vtmode),
1729                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1730                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1731                       NULL);
1732     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1733                   HELPCTX(selection_linedraw),
1734                   conf_checkbox_handler, I(CONF_rawcnp));
1735
1736     /*
1737      * The Window/Selection panel.
1738      */
1739     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1740         
1741     s = ctrl_getset(b, "Window/Selection", "mouse",
1742                     "Control use of mouse");
1743     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1744                   HELPCTX(selection_shiftdrag),
1745                   conf_checkbox_handler, I(CONF_mouse_override));
1746     ctrl_radiobuttons(s,
1747                       "Default selection mode (Alt+drag does the other one):",
1748                       NO_SHORTCUT, 2,
1749                       HELPCTX(selection_rect),
1750                       conf_radiobutton_handler,
1751                       I(CONF_rect_select),
1752                       "Normal", 'n', I(0),
1753                       "Rectangular block", 'r', I(1), NULL);
1754
1755     s = ctrl_getset(b, "Window/Selection", "charclass",
1756                     "Control the select-one-word-at-a-time mode");
1757     ccd = (struct charclass_data *)
1758         ctrl_alloc(b, sizeof(struct charclass_data));
1759     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1760                                 HELPCTX(selection_charclasses),
1761                                 charclass_handler, P(ccd));
1762     ccd->listbox->listbox.multisel = 1;
1763     ccd->listbox->listbox.ncols = 4;
1764     ccd->listbox->listbox.percentages = snewn(4, int);
1765     ccd->listbox->listbox.percentages[0] = 15;
1766     ccd->listbox->listbox.percentages[1] = 25;
1767     ccd->listbox->listbox.percentages[2] = 20;
1768     ccd->listbox->listbox.percentages[3] = 40;
1769     ctrl_columns(s, 2, 67, 33);
1770     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1771                                 HELPCTX(selection_charclasses),
1772                                 charclass_handler, P(ccd), P(NULL));
1773     ccd->editbox->generic.column = 0;
1774     ccd->button = ctrl_pushbutton(s, "Set", 's',
1775                                   HELPCTX(selection_charclasses),
1776                                   charclass_handler, P(ccd));
1777     ccd->button->generic.column = 1;
1778     ctrl_columns(s, 1, 100);
1779
1780     /*
1781      * The Window/Colours panel.
1782      */
1783     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1784
1785     s = ctrl_getset(b, "Window/Colours", "general",
1786                     "General options for colour usage");
1787     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1788                   HELPCTX(colours_ansi),
1789                   conf_checkbox_handler, I(CONF_ansi_colour));
1790     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1791                   HELPCTX(colours_xterm256), conf_checkbox_handler,
1792                   I(CONF_xterm_256_colour));
1793     ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
1794                       HELPCTX(colours_bold),
1795                       conf_radiobutton_handler, I(CONF_bold_style),
1796                       "The font", I(1),
1797                       "The colour", I(2),
1798                       "Both", I(3),
1799                       NULL);
1800
1801     str = dupprintf("Adjust the precise colours %s displays", appname);
1802     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1803     sfree(str);
1804     ctrl_text(s, "Select a colour from the list, and then click the"
1805               " Modify button to change its appearance.",
1806               HELPCTX(colours_config));
1807     ctrl_columns(s, 2, 67, 33);
1808     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1809     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1810                                HELPCTX(colours_config), colour_handler, P(cd));
1811     cd->listbox->generic.column = 0;
1812     cd->listbox->listbox.height = 7;
1813     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1814     c->generic.column = 1;
1815     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1816                              colour_handler, P(cd), P(NULL));
1817     cd->redit->generic.column = 1;
1818     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1819                              colour_handler, P(cd), P(NULL));
1820     cd->gedit->generic.column = 1;
1821     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1822                              colour_handler, P(cd), P(NULL));
1823     cd->bedit->generic.column = 1;
1824     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1825                                  colour_handler, P(cd));
1826     cd->button->generic.column = 1;
1827     ctrl_columns(s, 1, 100);
1828
1829     /*
1830      * The Connection panel. This doesn't show up if we're in a
1831      * non-network utility such as pterm. We tell this by being
1832      * passed a protocol < 0.
1833      */
1834     if (protocol >= 0) {
1835         ctrl_settitle(b, "Connection", "Options controlling the connection");
1836
1837         s = ctrl_getset(b, "Connection", "keepalive",
1838                         "Sending of null packets to keep session active");
1839         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1840                      HELPCTX(connection_keepalive),
1841                      conf_editbox_handler, I(CONF_ping_interval),
1842                      I(-1));
1843
1844         if (!midsession) {
1845             s = ctrl_getset(b, "Connection", "tcp",
1846                             "Low-level TCP connection options");
1847             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1848                           'n', HELPCTX(connection_nodelay),
1849                           conf_checkbox_handler,
1850                           I(CONF_tcp_nodelay));
1851             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1852                           'p', HELPCTX(connection_tcpkeepalive),
1853                           conf_checkbox_handler,
1854                           I(CONF_tcp_keepalives));
1855 #ifndef NO_IPV6
1856             s = ctrl_getset(b, "Connection", "ipversion",
1857                           "Internet protocol version");
1858             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1859                           HELPCTX(connection_ipversion),
1860                           conf_radiobutton_handler,
1861                           I(CONF_addressfamily),
1862                           "Auto", 'u', I(ADDRTYPE_UNSPEC),
1863                           "IPv4", '4', I(ADDRTYPE_IPV4),
1864                           "IPv6", '6', I(ADDRTYPE_IPV6),
1865                           NULL);
1866 #endif
1867
1868             {
1869                 char *label = backend_from_proto(PROT_SSH) ?
1870                     "Logical name of remote host (e.g. for SSH key lookup):" :
1871                     "Logical name of remote host:";
1872                 s = ctrl_getset(b, "Connection", "identity",
1873                                 "Logical name of remote host");
1874                 ctrl_editbox(s, label, 'm', 100,
1875                              HELPCTX(connection_loghost),
1876                              conf_editbox_handler, I(CONF_loghost), I(1));
1877             }
1878         }
1879
1880         /*
1881          * A sub-panel Connection/Data, containing options that
1882          * decide on data to send to the server.
1883          */
1884         if (!midsession) {
1885             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1886
1887             s = ctrl_getset(b, "Connection/Data", "login",
1888                             "Login details");
1889             ctrl_editbox(s, "Auto-login username", 'u', 50,
1890                          HELPCTX(connection_username),
1891                          conf_editbox_handler, I(CONF_username), I(1));
1892             {
1893                 /* We assume the local username is sufficiently stable
1894                  * to include on the dialog box. */
1895                 char *user = get_username();
1896                 char *userlabel = dupprintf("Use system username (%s)",
1897                                             user ? user : "");
1898                 sfree(user);
1899                 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1900                                   HELPCTX(connection_username_from_env),
1901                                   conf_radiobutton_handler,
1902                                   I(CONF_username_from_env),
1903                                   "Prompt", I(FALSE),
1904                                   userlabel, I(TRUE),
1905                                   NULL);
1906                 sfree(userlabel);
1907             }
1908
1909             s = ctrl_getset(b, "Connection/Data", "term",
1910                             "Terminal details");
1911             ctrl_editbox(s, "Terminal-type string", 't', 50,
1912                          HELPCTX(connection_termtype),
1913                          conf_editbox_handler, I(CONF_termtype), I(1));
1914             ctrl_editbox(s, "Terminal speeds", 's', 50,
1915                          HELPCTX(connection_termspeed),
1916                          conf_editbox_handler, I(CONF_termspeed), I(1));
1917
1918             s = ctrl_getset(b, "Connection/Data", "env",
1919                             "Environment variables");
1920             ctrl_columns(s, 2, 80, 20);
1921             ed = (struct environ_data *)
1922                 ctrl_alloc(b, sizeof(struct environ_data));
1923             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1924                                       HELPCTX(telnet_environ),
1925                                       environ_handler, P(ed), P(NULL));
1926             ed->varbox->generic.column = 0;
1927             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1928                                       HELPCTX(telnet_environ),
1929                                       environ_handler, P(ed), P(NULL));
1930             ed->valbox->generic.column = 0;
1931             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1932                                             HELPCTX(telnet_environ),
1933                                             environ_handler, P(ed));
1934             ed->addbutton->generic.column = 1;
1935             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1936                                             HELPCTX(telnet_environ),
1937                                             environ_handler, P(ed));
1938             ed->rembutton->generic.column = 1;
1939             ctrl_columns(s, 1, 100);
1940             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1941                                        HELPCTX(telnet_environ),
1942                                        environ_handler, P(ed));
1943             ed->listbox->listbox.height = 3;
1944             ed->listbox->listbox.ncols = 2;
1945             ed->listbox->listbox.percentages = snewn(2, int);
1946             ed->listbox->listbox.percentages[0] = 30;
1947             ed->listbox->listbox.percentages[1] = 70;
1948         }
1949
1950     }
1951
1952     if (!midsession) {
1953         /*
1954          * The Connection/Proxy panel.
1955          */
1956         ctrl_settitle(b, "Connection/Proxy",
1957                       "Options controlling proxy usage");
1958
1959         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1960         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1961                           HELPCTX(proxy_type),
1962                           conf_radiobutton_handler,
1963                           I(CONF_proxy_type),
1964                           "None", I(PROXY_NONE),
1965                           "SOCKS 4", I(PROXY_SOCKS4),
1966                           "SOCKS 5", I(PROXY_SOCKS5),
1967                           "HTTP", I(PROXY_HTTP),
1968                           "Telnet", I(PROXY_TELNET),
1969                           NULL);
1970         ctrl_columns(s, 2, 80, 20);
1971         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1972                          HELPCTX(proxy_main),
1973                          conf_editbox_handler,
1974                          I(CONF_proxy_host), I(1));
1975         c->generic.column = 0;
1976         c = ctrl_editbox(s, "Port", 'p', 100,
1977                          HELPCTX(proxy_main),
1978                          conf_editbox_handler,
1979                          I(CONF_proxy_port),
1980                          I(-1));
1981         c->generic.column = 1;
1982         ctrl_columns(s, 1, 100);
1983         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1984                      HELPCTX(proxy_exclude),
1985                      conf_editbox_handler,
1986                      I(CONF_proxy_exclude_list), I(1));
1987         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1988                       HELPCTX(proxy_exclude),
1989                       conf_checkbox_handler,
1990                       I(CONF_even_proxy_localhost));
1991         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1992                           HELPCTX(proxy_dns),
1993                           conf_radiobutton_handler,
1994                           I(CONF_proxy_dns),
1995                           "No", I(FORCE_OFF),
1996                           "Auto", I(AUTO),
1997                           "Yes", I(FORCE_ON), NULL);
1998         ctrl_editbox(s, "Username", 'u', 60,
1999                      HELPCTX(proxy_auth),
2000                      conf_editbox_handler,
2001                      I(CONF_proxy_username), I(1));
2002         c = ctrl_editbox(s, "Password", 'w', 60,
2003                          HELPCTX(proxy_auth),
2004                          conf_editbox_handler,
2005                          I(CONF_proxy_password), I(1));
2006         c->editbox.password = 1;
2007         ctrl_editbox(s, "Telnet command", 'm', 100,
2008                      HELPCTX(proxy_command),
2009                      conf_editbox_handler,
2010                      I(CONF_proxy_telnet_command), I(1));
2011     }
2012
2013     /*
2014      * The Telnet panel exists in the base config box, and in a
2015      * mid-session reconfig box _if_ we're using Telnet.
2016      */
2017     if (!midsession || protocol == PROT_TELNET) {
2018         /*
2019          * The Connection/Telnet panel.
2020          */
2021         ctrl_settitle(b, "Connection/Telnet",
2022                       "Options controlling Telnet connections");
2023
2024         s = ctrl_getset(b, "Connection/Telnet", "protocol",
2025                         "Telnet protocol adjustments");
2026
2027         if (!midsession) {
2028             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2029                               NO_SHORTCUT, 2,
2030                               HELPCTX(telnet_oldenviron),
2031                               conf_radiobutton_handler,
2032                               I(CONF_rfc_environ),
2033                               "BSD (commonplace)", 'b', I(0),
2034                               "RFC 1408 (unusual)", 'f', I(1), NULL);
2035             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2036                               HELPCTX(telnet_passive),
2037                               conf_radiobutton_handler,
2038                               I(CONF_passive_telnet),
2039                               "Passive", I(1), "Active", I(0), NULL);
2040         }
2041         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2042                       HELPCTX(telnet_specialkeys),
2043                       conf_checkbox_handler,
2044                       I(CONF_telnet_keyboard));
2045         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2046                       'm', HELPCTX(telnet_newline),
2047                       conf_checkbox_handler,
2048                       I(CONF_telnet_newline));
2049     }
2050
2051     if (!midsession) {
2052
2053         /*
2054          * The Connection/Rlogin panel.
2055          */
2056         ctrl_settitle(b, "Connection/Rlogin",
2057                       "Options controlling Rlogin connections");
2058
2059         s = ctrl_getset(b, "Connection/Rlogin", "data",
2060                         "Data to send to the server");
2061         ctrl_editbox(s, "Local username:", 'l', 50,
2062                      HELPCTX(rlogin_localuser),
2063                      conf_editbox_handler, I(CONF_localusername), I(1));
2064
2065     }
2066
2067     /*
2068      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2069      * when we're not doing SSH.
2070      */
2071
2072     if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2073
2074         /*
2075          * The Connection/SSH panel.
2076          */
2077         ctrl_settitle(b, "Connection/SSH",
2078                       "Options controlling SSH connections");
2079
2080         if (midsession && protcfginfo == 1) {
2081             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2082             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2083                       "session; it is only here so that sub-panels of it can "
2084                       "exist without looking strange.", HELPCTX(no_help));
2085         }
2086
2087         if (!midsession) {
2088
2089             s = ctrl_getset(b, "Connection/SSH", "data",
2090                             "Data to send to the server");
2091             ctrl_editbox(s, "Remote command:", 'r', 100,
2092                          HELPCTX(ssh_command),
2093                          conf_editbox_handler, I(CONF_remote_cmd), I(1));
2094
2095             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2096             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2097                           HELPCTX(ssh_noshell),
2098                           conf_checkbox_handler,
2099                           I(CONF_ssh_no_shell));
2100         }
2101
2102         if (!midsession || protcfginfo != 1) {
2103             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2104
2105             ctrl_checkbox(s, "Enable compression", 'e',
2106                           HELPCTX(ssh_compress),
2107                           conf_checkbox_handler,
2108                           I(CONF_compression));
2109         }
2110
2111         if (!midsession) {
2112             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2113
2114             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2115                               HELPCTX(ssh_protocol),
2116                               conf_radiobutton_handler,
2117                               I(CONF_sshprot),
2118                               "1 only", 'l', I(0),
2119                               "1", '1', I(1),
2120                               "2", '2', I(2),
2121                               "2 only", 'y', I(3), NULL);
2122         }
2123
2124         if (!midsession || protcfginfo != 1) {
2125             s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
2126             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2127                               HELPCTX(ssh_ciphers),
2128                               cipherlist_handler, P(NULL));
2129             c->listbox.height = 6;
2130
2131             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2132                           HELPCTX(ssh_ciphers),
2133                           conf_checkbox_handler,
2134                           I(CONF_ssh2_des_cbc));
2135         }
2136
2137         /*
2138          * The Connection/SSH/Kex panel. (Owing to repeat key
2139          * exchange, this is all meaningful in mid-session _if_
2140          * we're using SSH-2 or haven't decided yet.)
2141          */
2142         if (protcfginfo != 1) {
2143             ctrl_settitle(b, "Connection/SSH/Kex",
2144                           "Options controlling SSH key exchange");
2145
2146             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2147                             "Key exchange algorithm options");
2148             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2149                               HELPCTX(ssh_kexlist),
2150                               kexlist_handler, P(NULL));
2151             c->listbox.height = 5;
2152
2153             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2154                             "Options controlling key re-exchange");
2155
2156             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2157                          HELPCTX(ssh_kex_repeat),
2158                          conf_editbox_handler,
2159                          I(CONF_ssh_rekey_time),
2160                          I(-1));
2161             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2162                          HELPCTX(ssh_kex_repeat),
2163                          conf_editbox_handler,
2164                          I(CONF_ssh_rekey_data),
2165                          I(16));
2166             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2167                       HELPCTX(ssh_kex_repeat));
2168         }
2169
2170         if (!midsession) {
2171
2172             /*
2173              * The Connection/SSH/Auth panel.
2174              */
2175             ctrl_settitle(b, "Connection/SSH/Auth",
2176                           "Options controlling SSH authentication");
2177
2178             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2179             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2180                           HELPCTX(ssh_auth_bypass),
2181                           conf_checkbox_handler,
2182                           I(CONF_ssh_no_userauth));
2183             ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2184                           'd', HELPCTX(ssh_auth_banner),
2185                           conf_checkbox_handler,
2186                           I(CONF_ssh_show_banner));
2187
2188             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2189                             "Authentication methods");
2190             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2191                           HELPCTX(ssh_auth_pageant),
2192                           conf_checkbox_handler,
2193                           I(CONF_tryagent));
2194             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2195                           HELPCTX(ssh_auth_tis),
2196                           conf_checkbox_handler,
2197                           I(CONF_try_tis_auth));
2198             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2199                           'i', HELPCTX(ssh_auth_ki),
2200                           conf_checkbox_handler,
2201                           I(CONF_try_ki_auth));
2202
2203             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2204                             "Authentication parameters");
2205             ctrl_checkbox(s, "Allow agent forwarding", 'f',
2206                           HELPCTX(ssh_auth_agentfwd),
2207                           conf_checkbox_handler, I(CONF_agentfwd));
2208             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2209                           HELPCTX(ssh_auth_changeuser),
2210                           conf_checkbox_handler,
2211                           I(CONF_change_username));
2212             ctrl_filesel(s, "Private key file for authentication:", 'k',
2213                          FILTER_KEY_FILES, FALSE, "Select private key file",
2214                          HELPCTX(ssh_auth_privkey),
2215                          conf_filesel_handler, I(CONF_keyfile));
2216
2217 #ifndef NO_GSSAPI
2218             /*
2219              * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2220              * the main Auth panel.
2221              */
2222             ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2223                           "Options controlling GSSAPI authentication");
2224             s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2225
2226             ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2227                           't', HELPCTX(ssh_gssapi),
2228                           conf_checkbox_handler,
2229                           I(CONF_try_gssapi_auth));
2230
2231             ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2232                           HELPCTX(ssh_gssapi_delegation),
2233                           conf_checkbox_handler,
2234                           I(CONF_gssapifwd));
2235
2236             /*
2237              * GSSAPI library selection.
2238              */
2239             if (ngsslibs > 1) {
2240                 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2241                                   'p', HELPCTX(ssh_gssapi_libraries),
2242                                   gsslist_handler, P(NULL));
2243                 c->listbox.height = ngsslibs;
2244
2245                 /*
2246                  * I currently assume that if more than one GSS
2247                  * library option is available, then one of them is
2248                  * 'user-supplied' and so we should present the
2249                  * following file selector. This is at least half-
2250                  * reasonable, because if we're using statically
2251                  * linked GSSAPI then there will only be one option
2252                  * and no way to load from a user-supplied library,
2253                  * whereas if we're using dynamic libraries then
2254                  * there will almost certainly be some default
2255                  * option in addition to a user-supplied path. If
2256                  * anyone ever ports PuTTY to a system on which
2257                  * dynamic-library GSSAPI is available but there is
2258                  * absolutely no consensus on where to keep the
2259                  * libraries, there'll need to be a flag alongside
2260                  * ngsslibs to control whether the file selector is
2261                  * displayed. 
2262                  */
2263
2264                 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2265                              FILTER_DYNLIB_FILES, FALSE, "Select library file",
2266                              HELPCTX(ssh_gssapi_libraries),
2267                              conf_filesel_handler,
2268                              I(CONF_ssh_gss_custom));
2269             }
2270 #endif
2271         }
2272
2273         if (!midsession) {
2274             /*
2275              * The Connection/SSH/TTY panel.
2276              */
2277             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2278
2279             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2280             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2281                           HELPCTX(ssh_nopty),
2282                           conf_checkbox_handler,
2283                           I(CONF_nopty));
2284
2285             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2286                             "Terminal modes");
2287             td = (struct ttymodes_data *)
2288                 ctrl_alloc(b, sizeof(struct ttymodes_data));
2289             ctrl_columns(s, 2, 75, 25);
2290             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2291             c->generic.column = 0;
2292             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2293                                             HELPCTX(ssh_ttymodes),
2294                                             ttymodes_handler, P(td));
2295             td->rembutton->generic.column = 1;
2296             td->rembutton->generic.tabdelay = 1;
2297             ctrl_columns(s, 1, 100);
2298             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2299                                        HELPCTX(ssh_ttymodes),
2300                                        ttymodes_handler, P(td));
2301             td->listbox->listbox.multisel = 1;
2302             td->listbox->listbox.height = 4;
2303             td->listbox->listbox.ncols = 2;
2304             td->listbox->listbox.percentages = snewn(2, int);
2305             td->listbox->listbox.percentages[0] = 40;
2306             td->listbox->listbox.percentages[1] = 60;
2307             ctrl_tabdelay(s, td->rembutton);
2308             ctrl_columns(s, 2, 75, 25);
2309             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2310                                          HELPCTX(ssh_ttymodes),
2311                                          ttymodes_handler, P(td));
2312             td->modelist->generic.column = 0;
2313             td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2314                                             HELPCTX(ssh_ttymodes),
2315                                             ttymodes_handler, P(td));
2316             td->addbutton->generic.column = 1;
2317             td->addbutton->generic.tabdelay = 1;
2318             ctrl_columns(s, 1, 100);        /* column break */
2319             /* Bit of a hack to get the value radio buttons and
2320              * edit-box on the same row. */
2321             ctrl_columns(s, 3, 25, 50, 25);
2322             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2323             c->generic.column = 0;
2324             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2325                                              HELPCTX(ssh_ttymodes),
2326                                              ttymodes_handler, P(td),
2327                                              "Auto", NO_SHORTCUT, P(NULL),
2328                                              "This:", NO_SHORTCUT, P(NULL),
2329                                              NULL);
2330             td->valradio->generic.column = 1;
2331             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2332                                       HELPCTX(ssh_ttymodes),
2333                                       ttymodes_handler, P(td), P(NULL));
2334             td->valbox->generic.column = 2;
2335             ctrl_tabdelay(s, td->addbutton);
2336
2337         }
2338
2339         if (!midsession) {
2340             /*
2341              * The Connection/SSH/X11 panel.
2342              */
2343             ctrl_settitle(b, "Connection/SSH/X11",
2344                           "Options controlling SSH X11 forwarding");
2345
2346             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2347             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2348                           HELPCTX(ssh_tunnels_x11),
2349                           conf_checkbox_handler,I(CONF_x11_forward));
2350             ctrl_editbox(s, "X display location", 'x', 50,
2351                          HELPCTX(ssh_tunnels_x11),
2352                          conf_editbox_handler, I(CONF_x11_display), I(1));
2353             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2354                               HELPCTX(ssh_tunnels_x11auth),
2355                               conf_radiobutton_handler,
2356                               I(CONF_x11_auth),
2357                               "MIT-Magic-Cookie-1", I(X11_MIT),
2358                               "XDM-Authorization-1", I(X11_XDM), NULL);
2359         }
2360
2361         /*
2362          * The Tunnels panel _is_ still available in mid-session.
2363          */
2364         ctrl_settitle(b, "Connection/SSH/Tunnels",
2365                       "Options controlling SSH port forwarding");
2366
2367         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2368                         "Port forwarding");
2369         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2370                       HELPCTX(ssh_tunnels_portfwd_localhost),
2371                       conf_checkbox_handler,
2372                       I(CONF_lport_acceptall));
2373         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2374                       HELPCTX(ssh_tunnels_portfwd_localhost),
2375                       conf_checkbox_handler,
2376                       I(CONF_rport_acceptall));
2377
2378         ctrl_columns(s, 3, 55, 20, 25);
2379         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2380         c->generic.column = COLUMN_FIELD(0,2);
2381         /* You want to select from the list, _then_ hit Remove. So tab order
2382          * should be that way round. */
2383         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2384         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2385                                          HELPCTX(ssh_tunnels_portfwd),
2386                                          portfwd_handler, P(pfd));
2387         pfd->rembutton->generic.column = 2;
2388         pfd->rembutton->generic.tabdelay = 1;
2389         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2390                                     HELPCTX(ssh_tunnels_portfwd),
2391                                     portfwd_handler, P(pfd));
2392         pfd->listbox->listbox.height = 3;
2393         pfd->listbox->listbox.ncols = 2;
2394         pfd->listbox->listbox.percentages = snewn(2, int);
2395         pfd->listbox->listbox.percentages[0] = 20;
2396         pfd->listbox->listbox.percentages[1] = 80;
2397         ctrl_tabdelay(s, pfd->rembutton);
2398         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2399         /* You want to enter source, destination and type, _then_ hit Add.
2400          * Again, we adjust the tab order to reflect this. */
2401         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2402                                          HELPCTX(ssh_tunnels_portfwd),
2403                                          portfwd_handler, P(pfd));
2404         pfd->addbutton->generic.column = 2;
2405         pfd->addbutton->generic.tabdelay = 1;
2406         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2407                                       HELPCTX(ssh_tunnels_portfwd),
2408                                       portfwd_handler, P(pfd), P(NULL));
2409         pfd->sourcebox->generic.column = 0;
2410         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2411                                     HELPCTX(ssh_tunnels_portfwd),
2412                                     portfwd_handler, P(pfd), P(NULL));
2413         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2414                                            HELPCTX(ssh_tunnels_portfwd),
2415                                            portfwd_handler, P(pfd),
2416                                            "Local", 'l', P(NULL),
2417                                            "Remote", 'm', P(NULL),
2418                                            "Dynamic", 'y', P(NULL),
2419                                            NULL);
2420 #ifndef NO_IPV6
2421         pfd->addressfamily =
2422             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2423                               HELPCTX(ssh_tunnels_portfwd_ipversion),
2424                               portfwd_handler, P(pfd),
2425                               "Auto", 'u', I(ADDRTYPE_UNSPEC),
2426                               "IPv4", '4', I(ADDRTYPE_IPV4),
2427                               "IPv6", '6', I(ADDRTYPE_IPV6),
2428                               NULL);
2429 #endif
2430         ctrl_tabdelay(s, pfd->addbutton);
2431         ctrl_columns(s, 1, 100);
2432
2433         if (!midsession) {
2434             /*
2435              * The Connection/SSH/Bugs panel.
2436              */
2437             ctrl_settitle(b, "Connection/SSH/Bugs",
2438                           "Workarounds for SSH server bugs");
2439
2440             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2441                             "Detection of known bugs in SSH servers");
2442             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2443                           HELPCTX(ssh_bugs_ignore1),
2444                           sshbug_handler, I(CONF_sshbug_ignore1));
2445             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2446                           HELPCTX(ssh_bugs_plainpw1),
2447                           sshbug_handler, I(CONF_sshbug_plainpw1));
2448             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2449                           HELPCTX(ssh_bugs_rsa1),
2450                           sshbug_handler, I(CONF_sshbug_rsa1));
2451             ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2452                           HELPCTX(ssh_bugs_ignore2),
2453                           sshbug_handler, I(CONF_sshbug_ignore2));
2454             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2455                           HELPCTX(ssh_bugs_hmac2),
2456                           sshbug_handler, I(CONF_sshbug_hmac2));
2457             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2458                           HELPCTX(ssh_bugs_derivekey2),
2459                           sshbug_handler, I(CONF_sshbug_derivekey2));
2460             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2461                           HELPCTX(ssh_bugs_rsapad2),
2462                           sshbug_handler, I(CONF_sshbug_rsapad2));
2463             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2464                           HELPCTX(ssh_bugs_pksessid2),
2465                           sshbug_handler, I(CONF_sshbug_pksessid2));
2466             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2467                           HELPCTX(ssh_bugs_rekey2),
2468                           sshbug_handler, I(CONF_sshbug_rekey2));
2469             ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2470                           HELPCTX(ssh_bugs_maxpkt2),
2471                           sshbug_handler, I(CONF_sshbug_maxpkt2));
2472         }
2473     }
2474 }