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