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