]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Fallout from the big revamp in r9214: colour handling was going a bit
[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_checkbox(s, "Bolded text is a different colour", 'b',
1794                   HELPCTX(colours_bold),
1795                   conf_checkbox_handler, I(CONF_bold_colour));
1796
1797     str = dupprintf("Adjust the precise colours %s displays", appname);
1798     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1799     sfree(str);
1800     ctrl_text(s, "Select a colour from the list, and then click the"
1801               " Modify button to change its appearance.",
1802               HELPCTX(colours_config));
1803     ctrl_columns(s, 2, 67, 33);
1804     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1805     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1806                                HELPCTX(colours_config), colour_handler, P(cd));
1807     cd->listbox->generic.column = 0;
1808     cd->listbox->listbox.height = 7;
1809     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1810     c->generic.column = 1;
1811     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1812                              colour_handler, P(cd), P(NULL));
1813     cd->redit->generic.column = 1;
1814     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1815                              colour_handler, P(cd), P(NULL));
1816     cd->gedit->generic.column = 1;
1817     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1818                              colour_handler, P(cd), P(NULL));
1819     cd->bedit->generic.column = 1;
1820     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1821                                  colour_handler, P(cd));
1822     cd->button->generic.column = 1;
1823     ctrl_columns(s, 1, 100);
1824
1825     /*
1826      * The Connection panel. This doesn't show up if we're in a
1827      * non-network utility such as pterm. We tell this by being
1828      * passed a protocol < 0.
1829      */
1830     if (protocol >= 0) {
1831         ctrl_settitle(b, "Connection", "Options controlling the connection");
1832
1833         s = ctrl_getset(b, "Connection", "keepalive",
1834                         "Sending of null packets to keep session active");
1835         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1836                      HELPCTX(connection_keepalive),
1837                      conf_editbox_handler, I(CONF_ping_interval),
1838                      I(-1));
1839
1840         if (!midsession) {
1841             s = ctrl_getset(b, "Connection", "tcp",
1842                             "Low-level TCP connection options");
1843             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1844                           'n', HELPCTX(connection_nodelay),
1845                           conf_checkbox_handler,
1846                           I(CONF_tcp_nodelay));
1847             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1848                           'p', HELPCTX(connection_tcpkeepalive),
1849                           conf_checkbox_handler,
1850                           I(CONF_tcp_keepalives));
1851 #ifndef NO_IPV6
1852             s = ctrl_getset(b, "Connection", "ipversion",
1853                           "Internet protocol version");
1854             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1855                           HELPCTX(connection_ipversion),
1856                           conf_radiobutton_handler,
1857                           I(CONF_addressfamily),
1858                           "Auto", 'u', I(ADDRTYPE_UNSPEC),
1859                           "IPv4", '4', I(ADDRTYPE_IPV4),
1860                           "IPv6", '6', I(ADDRTYPE_IPV6),
1861                           NULL);
1862 #endif
1863
1864             {
1865                 char *label = backend_from_proto(PROT_SSH) ?
1866                     "Logical name of remote host (e.g. for SSH key lookup):" :
1867                     "Logical name of remote host:";
1868                 s = ctrl_getset(b, "Connection", "identity",
1869                                 "Logical name of remote host");
1870                 ctrl_editbox(s, label, 'm', 100,
1871                              HELPCTX(connection_loghost),
1872                              conf_editbox_handler, I(CONF_loghost), I(1));
1873             }
1874         }
1875
1876         /*
1877          * A sub-panel Connection/Data, containing options that
1878          * decide on data to send to the server.
1879          */
1880         if (!midsession) {
1881             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1882
1883             s = ctrl_getset(b, "Connection/Data", "login",
1884                             "Login details");
1885             ctrl_editbox(s, "Auto-login username", 'u', 50,
1886                          HELPCTX(connection_username),
1887                          conf_editbox_handler, I(CONF_username), I(1));
1888             {
1889                 /* We assume the local username is sufficiently stable
1890                  * to include on the dialog box. */
1891                 char *user = get_username();
1892                 char *userlabel = dupprintf("Use system username (%s)",
1893                                             user ? user : "");
1894                 sfree(user);
1895                 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1896                                   HELPCTX(connection_username_from_env),
1897                                   conf_radiobutton_handler,
1898                                   I(CONF_username_from_env),
1899                                   "Prompt", I(FALSE),
1900                                   userlabel, I(TRUE),
1901                                   NULL);
1902                 sfree(userlabel);
1903             }
1904
1905             s = ctrl_getset(b, "Connection/Data", "term",
1906                             "Terminal details");
1907             ctrl_editbox(s, "Terminal-type string", 't', 50,
1908                          HELPCTX(connection_termtype),
1909                          conf_editbox_handler, I(CONF_termtype), I(1));
1910             ctrl_editbox(s, "Terminal speeds", 's', 50,
1911                          HELPCTX(connection_termspeed),
1912                          conf_editbox_handler, I(CONF_termspeed), I(1));
1913
1914             s = ctrl_getset(b, "Connection/Data", "env",
1915                             "Environment variables");
1916             ctrl_columns(s, 2, 80, 20);
1917             ed = (struct environ_data *)
1918                 ctrl_alloc(b, sizeof(struct environ_data));
1919             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1920                                       HELPCTX(telnet_environ),
1921                                       environ_handler, P(ed), P(NULL));
1922             ed->varbox->generic.column = 0;
1923             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1924                                       HELPCTX(telnet_environ),
1925                                       environ_handler, P(ed), P(NULL));
1926             ed->valbox->generic.column = 0;
1927             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1928                                             HELPCTX(telnet_environ),
1929                                             environ_handler, P(ed));
1930             ed->addbutton->generic.column = 1;
1931             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1932                                             HELPCTX(telnet_environ),
1933                                             environ_handler, P(ed));
1934             ed->rembutton->generic.column = 1;
1935             ctrl_columns(s, 1, 100);
1936             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1937                                        HELPCTX(telnet_environ),
1938                                        environ_handler, P(ed));
1939             ed->listbox->listbox.height = 3;
1940             ed->listbox->listbox.ncols = 2;
1941             ed->listbox->listbox.percentages = snewn(2, int);
1942             ed->listbox->listbox.percentages[0] = 30;
1943             ed->listbox->listbox.percentages[1] = 70;
1944         }
1945
1946     }
1947
1948     if (!midsession) {
1949         /*
1950          * The Connection/Proxy panel.
1951          */
1952         ctrl_settitle(b, "Connection/Proxy",
1953                       "Options controlling proxy usage");
1954
1955         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1956         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1957                           HELPCTX(proxy_type),
1958                           conf_radiobutton_handler,
1959                           I(CONF_proxy_type),
1960                           "None", I(PROXY_NONE),
1961                           "SOCKS 4", I(PROXY_SOCKS4),
1962                           "SOCKS 5", I(PROXY_SOCKS5),
1963                           "HTTP", I(PROXY_HTTP),
1964                           "Telnet", I(PROXY_TELNET),
1965                           NULL);
1966         ctrl_columns(s, 2, 80, 20);
1967         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1968                          HELPCTX(proxy_main),
1969                          conf_editbox_handler,
1970                          I(CONF_proxy_host), I(1));
1971         c->generic.column = 0;
1972         c = ctrl_editbox(s, "Port", 'p', 100,
1973                          HELPCTX(proxy_main),
1974                          conf_editbox_handler,
1975                          I(CONF_proxy_port),
1976                          I(-1));
1977         c->generic.column = 1;
1978         ctrl_columns(s, 1, 100);
1979         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1980                      HELPCTX(proxy_exclude),
1981                      conf_editbox_handler,
1982                      I(CONF_proxy_exclude_list), I(1));
1983         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1984                       HELPCTX(proxy_exclude),
1985                       conf_checkbox_handler,
1986                       I(CONF_even_proxy_localhost));
1987         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1988                           HELPCTX(proxy_dns),
1989                           conf_radiobutton_handler,
1990                           I(CONF_proxy_dns),
1991                           "No", I(FORCE_OFF),
1992                           "Auto", I(AUTO),
1993                           "Yes", I(FORCE_ON), NULL);
1994         ctrl_editbox(s, "Username", 'u', 60,
1995                      HELPCTX(proxy_auth),
1996                      conf_editbox_handler,
1997                      I(CONF_proxy_username), I(1));
1998         c = ctrl_editbox(s, "Password", 'w', 60,
1999                          HELPCTX(proxy_auth),
2000                          conf_editbox_handler,
2001                          I(CONF_proxy_password), I(1));
2002         c->editbox.password = 1;
2003         ctrl_editbox(s, "Telnet command", 'm', 100,
2004                      HELPCTX(proxy_command),
2005                      conf_editbox_handler,
2006                      I(CONF_proxy_telnet_command), I(1));
2007     }
2008
2009     /*
2010      * The Telnet panel exists in the base config box, and in a
2011      * mid-session reconfig box _if_ we're using Telnet.
2012      */
2013     if (!midsession || protocol == PROT_TELNET) {
2014         /*
2015          * The Connection/Telnet panel.
2016          */
2017         ctrl_settitle(b, "Connection/Telnet",
2018                       "Options controlling Telnet connections");
2019
2020         s = ctrl_getset(b, "Connection/Telnet", "protocol",
2021                         "Telnet protocol adjustments");
2022
2023         if (!midsession) {
2024             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2025                               NO_SHORTCUT, 2,
2026                               HELPCTX(telnet_oldenviron),
2027                               conf_radiobutton_handler,
2028                               I(CONF_rfc_environ),
2029                               "BSD (commonplace)", 'b', I(0),
2030                               "RFC 1408 (unusual)", 'f', I(1), NULL);
2031             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2032                               HELPCTX(telnet_passive),
2033                               conf_radiobutton_handler,
2034                               I(CONF_passive_telnet),
2035                               "Passive", I(1), "Active", I(0), NULL);
2036         }
2037         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2038                       HELPCTX(telnet_specialkeys),
2039                       conf_checkbox_handler,
2040                       I(CONF_telnet_keyboard));
2041         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2042                       'm', HELPCTX(telnet_newline),
2043                       conf_checkbox_handler,
2044                       I(CONF_telnet_newline));
2045     }
2046
2047     if (!midsession) {
2048
2049         /*
2050          * The Connection/Rlogin panel.
2051          */
2052         ctrl_settitle(b, "Connection/Rlogin",
2053                       "Options controlling Rlogin connections");
2054
2055         s = ctrl_getset(b, "Connection/Rlogin", "data",
2056                         "Data to send to the server");
2057         ctrl_editbox(s, "Local username:", 'l', 50,
2058                      HELPCTX(rlogin_localuser),
2059                      conf_editbox_handler, I(CONF_localusername), I(1));
2060
2061     }
2062
2063     /*
2064      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2065      * when we're not doing SSH.
2066      */
2067
2068     if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2069
2070         /*
2071          * The Connection/SSH panel.
2072          */
2073         ctrl_settitle(b, "Connection/SSH",
2074                       "Options controlling SSH connections");
2075
2076         if (midsession && protcfginfo == 1) {
2077             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2078             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2079                       "session; it is only here so that sub-panels of it can "
2080                       "exist without looking strange.", HELPCTX(no_help));
2081         }
2082
2083         if (!midsession) {
2084
2085             s = ctrl_getset(b, "Connection/SSH", "data",
2086                             "Data to send to the server");
2087             ctrl_editbox(s, "Remote command:", 'r', 100,
2088                          HELPCTX(ssh_command),
2089                          conf_editbox_handler, I(CONF_remote_cmd), I(1));
2090
2091             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2092             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2093                           HELPCTX(ssh_noshell),
2094                           conf_checkbox_handler,
2095                           I(CONF_ssh_no_shell));
2096         }
2097
2098         if (!midsession || protcfginfo != 1) {
2099             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2100
2101             ctrl_checkbox(s, "Enable compression", 'e',
2102                           HELPCTX(ssh_compress),
2103                           conf_checkbox_handler,
2104                           I(CONF_compression));
2105         }
2106
2107         if (!midsession) {
2108             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2109
2110             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2111                               HELPCTX(ssh_protocol),
2112                               conf_radiobutton_handler,
2113                               I(CONF_sshprot),
2114                               "1 only", 'l', I(0),
2115                               "1", '1', I(1),
2116                               "2", '2', I(2),
2117                               "2 only", 'y', I(3), NULL);
2118         }
2119
2120         if (!midsession || protcfginfo != 1) {
2121             s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
2122             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2123                               HELPCTX(ssh_ciphers),
2124                               cipherlist_handler, P(NULL));
2125             c->listbox.height = 6;
2126
2127             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2128                           HELPCTX(ssh_ciphers),
2129                           conf_checkbox_handler,
2130                           I(CONF_ssh2_des_cbc));
2131         }
2132
2133         /*
2134          * The Connection/SSH/Kex panel. (Owing to repeat key
2135          * exchange, this is all meaningful in mid-session _if_
2136          * we're using SSH-2 or haven't decided yet.)
2137          */
2138         if (protcfginfo != 1) {
2139             ctrl_settitle(b, "Connection/SSH/Kex",
2140                           "Options controlling SSH key exchange");
2141
2142             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2143                             "Key exchange algorithm options");
2144             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2145                               HELPCTX(ssh_kexlist),
2146                               kexlist_handler, P(NULL));
2147             c->listbox.height = 5;
2148
2149             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2150                             "Options controlling key re-exchange");
2151
2152             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2153                          HELPCTX(ssh_kex_repeat),
2154                          conf_editbox_handler,
2155                          I(CONF_ssh_rekey_time),
2156                          I(-1));
2157             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2158                          HELPCTX(ssh_kex_repeat),
2159                          conf_editbox_handler,
2160                          I(CONF_ssh_rekey_data),
2161                          I(16));
2162             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2163                       HELPCTX(ssh_kex_repeat));
2164         }
2165
2166         if (!midsession) {
2167
2168             /*
2169              * The Connection/SSH/Auth panel.
2170              */
2171             ctrl_settitle(b, "Connection/SSH/Auth",
2172                           "Options controlling SSH authentication");
2173
2174             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2175             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2176                           HELPCTX(ssh_auth_bypass),
2177                           conf_checkbox_handler,
2178                           I(CONF_ssh_no_userauth));
2179             ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2180                           'd', HELPCTX(ssh_auth_banner),
2181                           conf_checkbox_handler,
2182                           I(CONF_ssh_show_banner));
2183
2184             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2185                             "Authentication methods");
2186             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2187                           HELPCTX(ssh_auth_pageant),
2188                           conf_checkbox_handler,
2189                           I(CONF_tryagent));
2190             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2191                           HELPCTX(ssh_auth_tis),
2192                           conf_checkbox_handler,
2193                           I(CONF_try_tis_auth));
2194             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2195                           'i', HELPCTX(ssh_auth_ki),
2196                           conf_checkbox_handler,
2197                           I(CONF_try_ki_auth));
2198
2199             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2200                             "Authentication parameters");
2201             ctrl_checkbox(s, "Allow agent forwarding", 'f',
2202                           HELPCTX(ssh_auth_agentfwd),
2203                           conf_checkbox_handler, I(CONF_agentfwd));
2204             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2205                           HELPCTX(ssh_auth_changeuser),
2206                           conf_checkbox_handler,
2207                           I(CONF_change_username));
2208             ctrl_filesel(s, "Private key file for authentication:", 'k',
2209                          FILTER_KEY_FILES, FALSE, "Select private key file",
2210                          HELPCTX(ssh_auth_privkey),
2211                          conf_filesel_handler, I(CONF_keyfile));
2212
2213 #ifndef NO_GSSAPI
2214             /*
2215              * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2216              * the main Auth panel.
2217              */
2218             ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2219                           "Options controlling GSSAPI authentication");
2220             s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2221
2222             ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2223                           't', HELPCTX(ssh_gssapi),
2224                           conf_checkbox_handler,
2225                           I(CONF_try_gssapi_auth));
2226
2227             ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2228                           HELPCTX(ssh_gssapi_delegation),
2229                           conf_checkbox_handler,
2230                           I(CONF_gssapifwd));
2231
2232             /*
2233              * GSSAPI library selection.
2234              */
2235             if (ngsslibs > 1) {
2236                 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2237                                   'p', HELPCTX(ssh_gssapi_libraries),
2238                                   gsslist_handler, P(NULL));
2239                 c->listbox.height = ngsslibs;
2240
2241                 /*
2242                  * I currently assume that if more than one GSS
2243                  * library option is available, then one of them is
2244                  * 'user-supplied' and so we should present the
2245                  * following file selector. This is at least half-
2246                  * reasonable, because if we're using statically
2247                  * linked GSSAPI then there will only be one option
2248                  * and no way to load from a user-supplied library,
2249                  * whereas if we're using dynamic libraries then
2250                  * there will almost certainly be some default
2251                  * option in addition to a user-supplied path. If
2252                  * anyone ever ports PuTTY to a system on which
2253                  * dynamic-library GSSAPI is available but there is
2254                  * absolutely no consensus on where to keep the
2255                  * libraries, there'll need to be a flag alongside
2256                  * ngsslibs to control whether the file selector is
2257                  * displayed. 
2258                  */
2259
2260                 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2261                              FILTER_DYNLIB_FILES, FALSE, "Select library file",
2262                              HELPCTX(ssh_gssapi_libraries),
2263                              conf_filesel_handler,
2264                              I(CONF_ssh_gss_custom));
2265             }
2266 #endif
2267         }
2268
2269         if (!midsession) {
2270             /*
2271              * The Connection/SSH/TTY panel.
2272              */
2273             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2274
2275             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2276             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2277                           HELPCTX(ssh_nopty),
2278                           conf_checkbox_handler,
2279                           I(CONF_nopty));
2280
2281             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2282                             "Terminal modes");
2283             td = (struct ttymodes_data *)
2284                 ctrl_alloc(b, sizeof(struct ttymodes_data));
2285             ctrl_columns(s, 2, 75, 25);
2286             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2287             c->generic.column = 0;
2288             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2289                                             HELPCTX(ssh_ttymodes),
2290                                             ttymodes_handler, P(td));
2291             td->rembutton->generic.column = 1;
2292             td->rembutton->generic.tabdelay = 1;
2293             ctrl_columns(s, 1, 100);
2294             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2295                                        HELPCTX(ssh_ttymodes),
2296                                        ttymodes_handler, P(td));
2297             td->listbox->listbox.multisel = 1;
2298             td->listbox->listbox.height = 4;
2299             td->listbox->listbox.ncols = 2;
2300             td->listbox->listbox.percentages = snewn(2, int);
2301             td->listbox->listbox.percentages[0] = 40;
2302             td->listbox->listbox.percentages[1] = 60;
2303             ctrl_tabdelay(s, td->rembutton);
2304             ctrl_columns(s, 2, 75, 25);
2305             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2306                                          HELPCTX(ssh_ttymodes),
2307                                          ttymodes_handler, P(td));
2308             td->modelist->generic.column = 0;
2309             td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2310                                             HELPCTX(ssh_ttymodes),
2311                                             ttymodes_handler, P(td));
2312             td->addbutton->generic.column = 1;
2313             td->addbutton->generic.tabdelay = 1;
2314             ctrl_columns(s, 1, 100);        /* column break */
2315             /* Bit of a hack to get the value radio buttons and
2316              * edit-box on the same row. */
2317             ctrl_columns(s, 3, 25, 50, 25);
2318             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2319             c->generic.column = 0;
2320             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2321                                              HELPCTX(ssh_ttymodes),
2322                                              ttymodes_handler, P(td),
2323                                              "Auto", NO_SHORTCUT, P(NULL),
2324                                              "This:", NO_SHORTCUT, P(NULL),
2325                                              NULL);
2326             td->valradio->generic.column = 1;
2327             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2328                                       HELPCTX(ssh_ttymodes),
2329                                       ttymodes_handler, P(td), P(NULL));
2330             td->valbox->generic.column = 2;
2331             ctrl_tabdelay(s, td->addbutton);
2332
2333         }
2334
2335         if (!midsession) {
2336             /*
2337              * The Connection/SSH/X11 panel.
2338              */
2339             ctrl_settitle(b, "Connection/SSH/X11",
2340                           "Options controlling SSH X11 forwarding");
2341
2342             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2343             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2344                           HELPCTX(ssh_tunnels_x11),
2345                           conf_checkbox_handler,I(CONF_x11_forward));
2346             ctrl_editbox(s, "X display location", 'x', 50,
2347                          HELPCTX(ssh_tunnels_x11),
2348                          conf_editbox_handler, I(CONF_x11_display), I(1));
2349             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2350                               HELPCTX(ssh_tunnels_x11auth),
2351                               conf_radiobutton_handler,
2352                               I(CONF_x11_auth),
2353                               "MIT-Magic-Cookie-1", I(X11_MIT),
2354                               "XDM-Authorization-1", I(X11_XDM), NULL);
2355         }
2356
2357         /*
2358          * The Tunnels panel _is_ still available in mid-session.
2359          */
2360         ctrl_settitle(b, "Connection/SSH/Tunnels",
2361                       "Options controlling SSH port forwarding");
2362
2363         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2364                         "Port forwarding");
2365         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2366                       HELPCTX(ssh_tunnels_portfwd_localhost),
2367                       conf_checkbox_handler,
2368                       I(CONF_lport_acceptall));
2369         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2370                       HELPCTX(ssh_tunnels_portfwd_localhost),
2371                       conf_checkbox_handler,
2372                       I(CONF_rport_acceptall));
2373
2374         ctrl_columns(s, 3, 55, 20, 25);
2375         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2376         c->generic.column = COLUMN_FIELD(0,2);
2377         /* You want to select from the list, _then_ hit Remove. So tab order
2378          * should be that way round. */
2379         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2380         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2381                                          HELPCTX(ssh_tunnels_portfwd),
2382                                          portfwd_handler, P(pfd));
2383         pfd->rembutton->generic.column = 2;
2384         pfd->rembutton->generic.tabdelay = 1;
2385         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2386                                     HELPCTX(ssh_tunnels_portfwd),
2387                                     portfwd_handler, P(pfd));
2388         pfd->listbox->listbox.height = 3;
2389         pfd->listbox->listbox.ncols = 2;
2390         pfd->listbox->listbox.percentages = snewn(2, int);
2391         pfd->listbox->listbox.percentages[0] = 20;
2392         pfd->listbox->listbox.percentages[1] = 80;
2393         ctrl_tabdelay(s, pfd->rembutton);
2394         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2395         /* You want to enter source, destination and type, _then_ hit Add.
2396          * Again, we adjust the tab order to reflect this. */
2397         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2398                                          HELPCTX(ssh_tunnels_portfwd),
2399                                          portfwd_handler, P(pfd));
2400         pfd->addbutton->generic.column = 2;
2401         pfd->addbutton->generic.tabdelay = 1;
2402         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2403                                       HELPCTX(ssh_tunnels_portfwd),
2404                                       portfwd_handler, P(pfd), P(NULL));
2405         pfd->sourcebox->generic.column = 0;
2406         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2407                                     HELPCTX(ssh_tunnels_portfwd),
2408                                     portfwd_handler, P(pfd), P(NULL));
2409         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2410                                            HELPCTX(ssh_tunnels_portfwd),
2411                                            portfwd_handler, P(pfd),
2412                                            "Local", 'l', P(NULL),
2413                                            "Remote", 'm', P(NULL),
2414                                            "Dynamic", 'y', P(NULL),
2415                                            NULL);
2416 #ifndef NO_IPV6
2417         pfd->addressfamily =
2418             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2419                               HELPCTX(ssh_tunnels_portfwd_ipversion),
2420                               portfwd_handler, P(pfd),
2421                               "Auto", 'u', I(ADDRTYPE_UNSPEC),
2422                               "IPv4", '4', I(ADDRTYPE_IPV4),
2423                               "IPv6", '6', I(ADDRTYPE_IPV6),
2424                               NULL);
2425 #endif
2426         ctrl_tabdelay(s, pfd->addbutton);
2427         ctrl_columns(s, 1, 100);
2428
2429         if (!midsession) {
2430             /*
2431              * The Connection/SSH/Bugs panel.
2432              */
2433             ctrl_settitle(b, "Connection/SSH/Bugs",
2434                           "Workarounds for SSH server bugs");
2435
2436             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2437                             "Detection of known bugs in SSH servers");
2438             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2439                           HELPCTX(ssh_bugs_ignore1),
2440                           sshbug_handler, I(CONF_sshbug_ignore1));
2441             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2442                           HELPCTX(ssh_bugs_plainpw1),
2443                           sshbug_handler, I(CONF_sshbug_plainpw1));
2444             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2445                           HELPCTX(ssh_bugs_rsa1),
2446                           sshbug_handler, I(CONF_sshbug_rsa1));
2447             ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2448                           HELPCTX(ssh_bugs_ignore2),
2449                           sshbug_handler, I(CONF_sshbug_ignore2));
2450             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2451                           HELPCTX(ssh_bugs_hmac2),
2452                           sshbug_handler, I(CONF_sshbug_hmac2));
2453             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2454                           HELPCTX(ssh_bugs_derivekey2),
2455                           sshbug_handler, I(CONF_sshbug_derivekey2));
2456             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2457                           HELPCTX(ssh_bugs_rsapad2),
2458                           sshbug_handler, I(CONF_sshbug_rsapad2));
2459             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2460                           HELPCTX(ssh_bugs_pksessid2),
2461                           sshbug_handler, I(CONF_sshbug_pksessid2));
2462             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2463                           HELPCTX(ssh_bugs_rekey2),
2464                           sshbug_handler, I(CONF_sshbug_rekey2));
2465             ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2466                           HELPCTX(ssh_bugs_maxpkt2),
2467                           sshbug_handler, I(CONF_sshbug_maxpkt2));
2468         }
2469     }
2470 }