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