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