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