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