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