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