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