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