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