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