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