]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
New SSH bug flag, for 'can't handle SSH2_MSG_IGNORE'. Another user
[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, *thiscp;
321         dlg_update_start(ctrl, dlg);
322         thiscp = cp_name(decode_codepage(cfg->line_codepage));
323         dlg_listbox_clear(ctrl, dlg);
324         for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
325             dlg_listbox_add(ctrl, dlg, cp);
326         dlg_editbox_set(ctrl, dlg, thiscp);
327         strcpy(cfg->line_codepage, thiscp);
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, clear = 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             clear = TRUE;
637             update = TRUE;
638         }
639     } else if (event == EVENT_SELCHANGE) {
640         if (ctrl == cd->listbox) {
641             /* The user has selected a colour. Update the RGB text. */
642             int i = dlg_listbox_index(ctrl, dlg);
643             if (i < 0) {
644                 clear = TRUE;
645             } else {
646                 clear = FALSE;
647                 r = cfg->colours[i][0];
648                 g = cfg->colours[i][1];
649                 b = cfg->colours[i][2];
650             }
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                 clear = FALSE;
704                 update = TRUE;
705             }
706         }
707     }
708
709     if (update) {
710         if (clear) {
711             dlg_editbox_set(cd->redit, dlg, "");
712             dlg_editbox_set(cd->gedit, dlg, "");
713             dlg_editbox_set(cd->bedit, dlg, "");
714         } else {
715             char buf[40];
716             sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
717             sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
718             sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
719         }
720     }
721 }
722
723 struct ttymodes_data {
724     union control *modelist, *valradio, *valbox;
725     union control *addbutton, *rembutton, *listbox;
726 };
727
728 static void ttymodes_handler(union control *ctrl, void *dlg,
729                              void *data, int event)
730 {
731     Config *cfg = (Config *)data;
732     struct ttymodes_data *td =
733         (struct ttymodes_data *)ctrl->generic.context.p;
734
735     if (event == EVENT_REFRESH) {
736         if (ctrl == td->listbox) {
737             char *p = cfg->ttymodes;
738             dlg_update_start(ctrl, dlg);
739             dlg_listbox_clear(ctrl, dlg);
740             while (*p) {
741                 int tabpos = strchr(p, '\t') - p;
742                 char *disp = dupprintf("%.*s\t%s", tabpos, p,
743                                        (p[tabpos+1] == 'A') ? "(auto)" :
744                                        p+tabpos+2);
745                 dlg_listbox_add(ctrl, dlg, disp);
746                 p += strlen(p) + 1;
747                 sfree(disp);
748             }
749             dlg_update_done(ctrl, dlg);
750         } else if (ctrl == td->modelist) {
751             int i;
752             dlg_update_start(ctrl, dlg);
753             dlg_listbox_clear(ctrl, dlg);
754             for (i = 0; ttymodes[i]; i++)
755                 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
756             dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
757             dlg_update_done(ctrl, dlg);
758         } else if (ctrl == td->valradio) {
759             dlg_radiobutton_set(ctrl, dlg, 0);
760         }
761     } else if (event == EVENT_ACTION) {
762         if (ctrl == td->addbutton) {
763             int ind = dlg_listbox_index(td->modelist, dlg);
764             if (ind >= 0) {
765                 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
766                 int slen, left;
767                 char *p, str[lenof(cfg->ttymodes)];
768                 /* Construct new entry */
769                 memset(str, 0, lenof(str));
770                 strncpy(str, ttymodes[ind], lenof(str)-3);
771                 slen = strlen(str);
772                 str[slen] = '\t';
773                 str[slen+1] = type;
774                 slen += 2;
775                 if (type == 'V') {
776                     dlg_editbox_get(td->valbox, dlg, str+slen, lenof(str)-slen);
777                 }
778                 /* Find end of list, deleting any existing instance */
779                 p = cfg->ttymodes;
780                 left = lenof(cfg->ttymodes);
781                 while (*p) {
782                     int t = strchr(p, '\t') - p;
783                     if (t == strlen(ttymodes[ind]) &&
784                         strncmp(p, ttymodes[ind], t) == 0) {
785                         memmove(p, p+strlen(p)+1, left - (strlen(p)+1));
786                         continue;
787                     }
788                     left -= strlen(p) + 1;
789                     p    += strlen(p) + 1;
790                 }
791                 /* Append new entry */
792                 memset(p, 0, left);
793                 strncpy(p, str, left - 2);
794                 dlg_refresh(td->listbox, dlg);
795             } else
796                 dlg_beep(dlg);
797         } else if (ctrl == td->rembutton) {
798             char *p = cfg->ttymodes;
799             int i = 0, len = lenof(cfg->ttymodes);
800             while (*p) {
801                 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
802                 if (dlg_listbox_issel(td->listbox, dlg, i)) {
803                     if (!multisel) {
804                         /* Populate controls with entry we're about to
805                          * delete, for ease of editing.
806                          * (If multiple entries were selected, don't
807                          * touch the controls.) */
808                         char *val = strchr(p, '\t');
809                         if (val) {
810                             int ind = 0;
811                             val++;
812                             while (ttymodes[ind]) {
813                                 if (strlen(ttymodes[ind]) == val-p-1 &&
814                                     !strncmp(ttymodes[ind], p, val-p-1))
815                                     break;
816                                 ind++;
817                             }
818                             dlg_listbox_select(td->modelist, dlg, ind);
819                             dlg_radiobutton_set(td->valradio, dlg,
820                                                 (*val == 'V'));
821                             dlg_editbox_set(td->valbox, dlg, val+1);
822                         }
823                     }
824                     memmove(p, p+strlen(p)+1, len - (strlen(p)+1));
825                     i++;
826                     continue;
827                 }
828                 len -= strlen(p) + 1;
829                 p   += strlen(p) + 1;
830                 i++;
831             }
832             memset(p, 0, lenof(cfg->ttymodes) - len);
833             dlg_refresh(td->listbox, dlg);
834         }
835     }
836 }
837
838 struct environ_data {
839     union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
840 };
841
842 static void environ_handler(union control *ctrl, void *dlg,
843                             void *data, int event)
844 {
845     Config *cfg = (Config *)data;
846     struct environ_data *ed =
847         (struct environ_data *)ctrl->generic.context.p;
848
849     if (event == EVENT_REFRESH) {
850         if (ctrl == ed->listbox) {
851             char *p = cfg->environmt;
852             dlg_update_start(ctrl, dlg);
853             dlg_listbox_clear(ctrl, dlg);
854             while (*p) {
855                 dlg_listbox_add(ctrl, dlg, p);
856                 p += strlen(p) + 1;
857             }
858             dlg_update_done(ctrl, dlg);
859         }
860     } else if (event == EVENT_ACTION) {
861         if (ctrl == ed->addbutton) {
862             char str[sizeof(cfg->environmt)];
863             char *p;
864             dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1);
865             if (!*str) {
866                 dlg_beep(dlg);
867                 return;
868             }
869             p = str + strlen(str);
870             *p++ = '\t';
871             dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str));
872             if (!*p) {
873                 dlg_beep(dlg);
874                 return;
875             }
876             p = cfg->environmt;
877             while (*p) {
878                 while (*p)
879                     p++;
880                 p++;
881             }
882             if ((p - cfg->environmt) + strlen(str) + 2 <
883                 sizeof(cfg->environmt)) {
884                 strcpy(p, str);
885                 p[strlen(str) + 1] = '\0';
886                 dlg_listbox_add(ed->listbox, dlg, str);
887                 dlg_editbox_set(ed->varbox, dlg, "");
888                 dlg_editbox_set(ed->valbox, dlg, "");
889             } else {
890                 dlg_error_msg(dlg, "Environment too big");
891             }
892         } else if (ctrl == ed->rembutton) {
893             int i = dlg_listbox_index(ed->listbox, dlg);
894             if (i < 0) {
895                 dlg_beep(dlg);
896             } else {
897                 char *p, *q, *str;
898
899                 dlg_listbox_del(ed->listbox, dlg, i);
900                 p = cfg->environmt;
901                 while (i > 0) {
902                     if (!*p)
903                         goto disaster;
904                     while (*p)
905                         p++;
906                     p++;
907                     i--;
908                 }
909                 q = p;
910                 if (!*p)
911                     goto disaster;
912                 /* Populate controls with the entry we're about to delete
913                  * for ease of editing */
914                 str = p;
915                 p = strchr(p, '\t');
916                 if (!p)
917                     goto disaster;
918                 *p = '\0';
919                 dlg_editbox_set(ed->varbox, dlg, str);
920                 p++;
921                 str = p;
922                 dlg_editbox_set(ed->valbox, dlg, str);
923                 p = strchr(p, '\0');
924                 if (!p)
925                     goto disaster;
926                 p++;
927                 while (*p) {
928                     while (*p)
929                         *q++ = *p++;
930                     *q++ = *p++;
931                 }
932                 *q = '\0';
933                 disaster:;
934             }
935         }
936     }
937 }
938
939 struct portfwd_data {
940     union control *addbutton, *rembutton, *listbox;
941     union control *sourcebox, *destbox, *direction;
942 #ifndef NO_IPV6
943     union control *addressfamily;
944 #endif
945 };
946
947 static void portfwd_handler(union control *ctrl, void *dlg,
948                             void *data, int event)
949 {
950     Config *cfg = (Config *)data;
951     struct portfwd_data *pfd =
952         (struct portfwd_data *)ctrl->generic.context.p;
953
954     if (event == EVENT_REFRESH) {
955         if (ctrl == pfd->listbox) {
956             char *p = cfg->portfwd;
957             dlg_update_start(ctrl, dlg);
958             dlg_listbox_clear(ctrl, dlg);
959             while (*p) {
960                 dlg_listbox_add(ctrl, dlg, p);
961                 p += strlen(p) + 1;
962             }
963             dlg_update_done(ctrl, dlg);
964         } else if (ctrl == pfd->direction) {
965             /*
966              * Default is Local.
967              */
968             dlg_radiobutton_set(ctrl, dlg, 0);
969 #ifndef NO_IPV6
970         } else if (ctrl == pfd->addressfamily) {
971             dlg_radiobutton_set(ctrl, dlg, 0);
972 #endif
973         }
974     } else if (event == EVENT_ACTION) {
975         if (ctrl == pfd->addbutton) {
976             char str[sizeof(cfg->portfwd)];
977             char *p;
978             int i, type;
979             int whichbutton;
980
981             i = 0;
982 #ifndef NO_IPV6
983             whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
984             if (whichbutton == 1)
985                 str[i++] = '4';
986             else if (whichbutton == 2)
987                 str[i++] = '6';
988 #endif
989
990             whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
991             if (whichbutton == 0)
992                 type = 'L';
993             else if (whichbutton == 1)
994                 type = 'R';
995             else
996                 type = 'D';
997             str[i++] = type;
998
999             dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i);
1000             if (!str[i]) {
1001                 dlg_error_msg(dlg, "You need to specify a source port number");
1002                 return;
1003             }
1004             p = str + strlen(str);
1005             if (type != 'D') {
1006                 *p++ = '\t';
1007                 dlg_editbox_get(pfd->destbox, dlg, p,
1008                                 sizeof(str) - (p - str));
1009                 if (!*p || !strchr(p, ':')) {
1010                     dlg_error_msg(dlg,
1011                                   "You need to specify a destination address\n"
1012                                   "in the form \"host.name:port\"");
1013                     return;
1014                 }
1015             } else
1016                 *p = '\0';
1017             p = cfg->portfwd;
1018             while (*p) {
1019                 if (strcmp(p,str) == 0) {
1020                     dlg_error_msg(dlg, "Specified forwarding already exists");
1021                     break;
1022                 }
1023                 while (*p)
1024                     p++;
1025                 p++;
1026             }
1027             if (!*p) {
1028                 if ((p - cfg->portfwd) + strlen(str) + 2 <=
1029                     sizeof(cfg->portfwd)) {
1030                     strcpy(p, str);
1031                     p[strlen(str) + 1] = '\0';
1032                     dlg_listbox_add(pfd->listbox, dlg, str);
1033                     dlg_editbox_set(pfd->sourcebox, dlg, "");
1034                     dlg_editbox_set(pfd->destbox, dlg, "");
1035                 } else {
1036                     dlg_error_msg(dlg, "Too many forwardings");
1037                 }
1038             }
1039         } else if (ctrl == pfd->rembutton) {
1040             int i = dlg_listbox_index(pfd->listbox, dlg);
1041             if (i < 0)
1042                 dlg_beep(dlg);
1043             else {
1044                 char *p, *q, *src, *dst;
1045                 char dir;
1046
1047                 dlg_listbox_del(pfd->listbox, dlg, i);
1048                 p = cfg->portfwd;
1049                 while (i > 0) {
1050                     if (!*p)
1051                         goto disaster2;
1052                     while (*p)
1053                         p++;
1054                     p++;
1055                     i--;
1056                 }
1057                 q = p;
1058                 if (!*p)
1059                     goto disaster2;
1060                 /* Populate the controls with the entry we're about to
1061                  * delete, for ease of editing. */
1062                 {
1063                     static const char *const afs = "A46";
1064                     char *afp = strchr(afs, *p);
1065 #ifndef NO_IPV6
1066                     int idx = afp ? afp-afs : 0;
1067 #endif
1068                     if (afp)
1069                         p++;
1070 #ifndef NO_IPV6
1071                     dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1072 #endif
1073                 }
1074                 {
1075                     static const char *const dirs = "LRD";
1076                     dir = *p;
1077                     dlg_radiobutton_set(pfd->direction, dlg,
1078                                         strchr(dirs, dir) - dirs);
1079                 }
1080                 p++;
1081                 if (dir != 'D') {
1082                     src = p;
1083                     p = strchr(p, '\t');
1084                     if (!p)
1085                         goto disaster2;
1086                     *p = '\0';
1087                     p++;
1088                     dst = p;
1089                 } else {
1090                     src = p;
1091                     dst = "";
1092                 }
1093                 p = strchr(p, '\0');
1094                 if (!p)
1095                     goto disaster2;
1096                 dlg_editbox_set(pfd->sourcebox, dlg, src);
1097                 dlg_editbox_set(pfd->destbox, dlg, dst);
1098                 p++;
1099                 while (*p) {
1100                     while (*p)
1101                         *q++ = *p++;
1102                     *q++ = *p++;
1103                 }
1104                 *q = '\0';
1105                 disaster2:;
1106             }
1107         }
1108     }
1109 }
1110
1111 void setup_config_box(struct controlbox *b, int midsession,
1112                       int protocol, int protcfginfo)
1113 {
1114     struct controlset *s;
1115     struct sessionsaver_data *ssd;
1116     struct charclass_data *ccd;
1117     struct colour_data *cd;
1118     struct ttymodes_data *td;
1119     struct environ_data *ed;
1120     struct portfwd_data *pfd;
1121     union control *c;
1122     char *str;
1123
1124     ssd = (struct sessionsaver_data *)
1125         ctrl_alloc(b, sizeof(struct sessionsaver_data));
1126     memset(ssd, 0, sizeof(*ssd));
1127     ssd->midsession = midsession;
1128
1129     /*
1130      * The standard panel that appears at the bottom of all panels:
1131      * Open, Cancel, Apply etc.
1132      */
1133     s = ctrl_getset(b, "", "", "");
1134     ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1135     ssd->okbutton = ctrl_pushbutton(s,
1136                                     (midsession ? "Apply" : "Open"),
1137                                     (char)(midsession ? 'a' : 'o'),
1138                                     HELPCTX(no_help),
1139                                     sessionsaver_handler, P(ssd));
1140     ssd->okbutton->button.isdefault = TRUE;
1141     ssd->okbutton->generic.column = 3;
1142     ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1143                                         sessionsaver_handler, P(ssd));
1144     ssd->cancelbutton->button.iscancel = TRUE;
1145     ssd->cancelbutton->generic.column = 4;
1146     /* We carefully don't close the 5-column part, so that platform-
1147      * specific add-ons can put extra buttons alongside Open and Cancel. */
1148
1149     /*
1150      * The Session panel.
1151      */
1152     str = dupprintf("Basic options for your %s session", appname);
1153     ctrl_settitle(b, "Session", str);
1154     sfree(str);
1155
1156     if (!midsession) {
1157         struct hostport *hp = (struct hostport *)
1158             ctrl_alloc(b, sizeof(struct hostport));
1159
1160         s = ctrl_getset(b, "Session", "hostport",
1161                         "Specify the destination you want to connect to");
1162         ctrl_columns(s, 2, 75, 25);
1163         c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1164                          HELPCTX(session_hostname),
1165                          config_host_handler, I(0), I(0));
1166         c->generic.column = 0;
1167         hp->host = c;
1168         c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1169                          HELPCTX(session_hostname),
1170                          config_port_handler, I(0), I(0));
1171         c->generic.column = 1;
1172         hp->port = c;
1173         ctrl_columns(s, 1, 100);
1174
1175         if (!backend_from_proto(PROT_SSH)) {
1176             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1177                               HELPCTX(session_hostname),
1178                               config_protocolbuttons_handler, P(hp),
1179                               "Raw", 'w', I(PROT_RAW),
1180                               "Telnet", 't', I(PROT_TELNET),
1181                               "Rlogin", 'i', I(PROT_RLOGIN),
1182                               NULL);
1183         } else {
1184             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1185                               HELPCTX(session_hostname),
1186                               config_protocolbuttons_handler, P(hp),
1187                               "Raw", 'w', I(PROT_RAW),
1188                               "Telnet", 't', I(PROT_TELNET),
1189                               "Rlogin", 'i', I(PROT_RLOGIN),
1190                               "SSH", 's', I(PROT_SSH),
1191                               NULL);
1192         }
1193     }
1194
1195     /*
1196      * The Load/Save panel is available even in mid-session.
1197      */
1198     s = ctrl_getset(b, "Session", "savedsessions",
1199                     midsession ? "Save the current session settings" :
1200                     "Load, save or delete a stored session");
1201     ctrl_columns(s, 2, 75, 25);
1202     get_sesslist(&ssd->sesslist, TRUE);
1203     ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1204                                 HELPCTX(session_saved),
1205                                 sessionsaver_handler, P(ssd), P(NULL));
1206     ssd->editbox->generic.column = 0;
1207     /* Reset columns so that the buttons are alongside the list, rather
1208      * than alongside that edit box. */
1209     ctrl_columns(s, 1, 100);
1210     ctrl_columns(s, 2, 75, 25);
1211     ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1212                                 HELPCTX(session_saved),
1213                                 sessionsaver_handler, P(ssd));
1214     ssd->listbox->generic.column = 0;
1215     ssd->listbox->listbox.height = 7;
1216     if (!midsession) {
1217         ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1218                                           HELPCTX(session_saved),
1219                                           sessionsaver_handler, P(ssd));
1220         ssd->loadbutton->generic.column = 1;
1221     } else {
1222         /* We can't offer the Load button mid-session, as it would allow the
1223          * user to load and subsequently save settings they can't see. (And
1224          * also change otherwise immutable settings underfoot; that probably
1225          * shouldn't be a problem, but.) */
1226         ssd->loadbutton = NULL;
1227     }
1228     /* "Save" button is permitted mid-session. */
1229     ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1230                                       HELPCTX(session_saved),
1231                                       sessionsaver_handler, P(ssd));
1232     ssd->savebutton->generic.column = 1;
1233     if (!midsession) {
1234         ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1235                                          HELPCTX(session_saved),
1236                                          sessionsaver_handler, P(ssd));
1237         ssd->delbutton->generic.column = 1;
1238     } else {
1239         /* Disable the Delete button mid-session too, for UI consistency. */
1240         ssd->delbutton = NULL;
1241     }
1242     ctrl_columns(s, 1, 100);
1243
1244     s = ctrl_getset(b, "Session", "otheropts", NULL);
1245     c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1246                           HELPCTX(session_coe),
1247                           dlg_stdradiobutton_handler,
1248                           I(offsetof(Config, close_on_exit)),
1249                           "Always", I(FORCE_ON),
1250                           "Never", I(FORCE_OFF),
1251                           "Only on clean exit", I(AUTO), NULL);
1252
1253     /*
1254      * The Session/Logging panel.
1255      */
1256     ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1257
1258     s = ctrl_getset(b, "Session/Logging", "main", NULL);
1259     /*
1260      * The logging buttons change depending on whether SSH packet
1261      * logging can sensibly be available.
1262      */
1263     {
1264         char *sshlogname, *sshrawlogname;
1265         if ((midsession && protocol == PROT_SSH) ||
1266             (!midsession && backend_from_proto(PROT_SSH))) {
1267             sshlogname = "SSH packets";
1268             sshrawlogname = "SSH packets and raw data";
1269         } else {
1270             sshlogname = NULL;         /* this will disable both buttons */
1271             sshrawlogname = NULL;      /* this will just placate optimisers */
1272         }
1273         ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1274                           HELPCTX(logging_main),
1275                           loggingbuttons_handler,
1276                           I(offsetof(Config, logtype)),
1277                           "None", 't', I(LGTYP_NONE),
1278                           "Printable output", 'p', I(LGTYP_ASCII),
1279                           "All session output", 'l', I(LGTYP_DEBUG),
1280                           sshlogname, 's', I(LGTYP_PACKETS),
1281                           sshrawlogname, 'r', I(LGTYP_SSHRAW),
1282                           NULL);
1283     }
1284     ctrl_filesel(s, "Log file name:", 'f',
1285                  NULL, TRUE, "Select session log file name",
1286                  HELPCTX(logging_filename),
1287                  dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));
1288     ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1289               " &T for time, and &H for host name)",
1290               HELPCTX(logging_filename));
1291     ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1292                       HELPCTX(logging_exists),
1293                       dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),
1294                       "Always overwrite it", I(LGXF_OVR),
1295                       "Always append to the end of it", I(LGXF_APN),
1296                       "Ask the user every time", I(LGXF_ASK), NULL);
1297     ctrl_checkbox(s, "Flush log file frequently", 'u',
1298                  HELPCTX(logging_flush),
1299                  dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));
1300
1301     if ((midsession && protocol == PROT_SSH) ||
1302         (!midsession && backend_from_proto(PROT_SSH))) {
1303         s = ctrl_getset(b, "Session/Logging", "ssh",
1304                         "Options specific to SSH packet logging");
1305         ctrl_checkbox(s, "Omit known password fields", 'k',
1306                       HELPCTX(logging_ssh_omit_password),
1307                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass)));
1308         ctrl_checkbox(s, "Omit session data", 'd',
1309                       HELPCTX(logging_ssh_omit_data),
1310                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata)));
1311     }
1312
1313     /*
1314      * The Terminal panel.
1315      */
1316     ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1317
1318     s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1319     ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1320                   HELPCTX(terminal_autowrap),
1321                   dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));
1322     ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1323                   HELPCTX(terminal_decom),
1324                   dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));
1325     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1326                   HELPCTX(terminal_lfhascr),
1327                   dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
1328     ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1329                   HELPCTX(terminal_crhaslf),
1330                   dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf)));
1331     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1332                   HELPCTX(terminal_bce),
1333                   dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
1334     ctrl_checkbox(s, "Enable blinking text", 'n',
1335                   HELPCTX(terminal_blink),
1336                   dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));
1337     ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1338                  HELPCTX(terminal_answerback),
1339                  dlg_stdeditbox_handler, I(offsetof(Config,answerback)),
1340                  I(sizeof(((Config *)0)->answerback)));
1341
1342     s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1343     ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1344                       HELPCTX(terminal_localecho),
1345                       dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),
1346                       "Auto", I(AUTO),
1347                       "Force on", I(FORCE_ON),
1348                       "Force off", I(FORCE_OFF), NULL);
1349     ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1350                       HELPCTX(terminal_localedit),
1351                       dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),
1352                       "Auto", I(AUTO),
1353                       "Force on", I(FORCE_ON),
1354                       "Force off", I(FORCE_OFF), NULL);
1355
1356     s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1357     ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1358                   HELPCTX(terminal_printing),
1359                   printerbox_handler, P(NULL), P(NULL));
1360
1361     /*
1362      * The Terminal/Keyboard panel.
1363      */
1364     ctrl_settitle(b, "Terminal/Keyboard",
1365                   "Options controlling the effects of keys");
1366
1367     s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1368                     "Change the sequences sent by:");
1369     ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1370                       HELPCTX(keyboard_backspace),
1371                       dlg_stdradiobutton_handler,
1372                       I(offsetof(Config, bksp_is_delete)),
1373                       "Control-H", I(0), "Control-? (127)", I(1), NULL);
1374     ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1375                       HELPCTX(keyboard_homeend),
1376                       dlg_stdradiobutton_handler,
1377                       I(offsetof(Config, rxvt_homeend)),
1378                       "Standard", I(0), "rxvt", I(1), NULL);
1379     ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1380                       HELPCTX(keyboard_funkeys),
1381                       dlg_stdradiobutton_handler,
1382                       I(offsetof(Config, funky_type)),
1383                       "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1384                       "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1385
1386     s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1387                     "Application keypad settings:");
1388     ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1389                       HELPCTX(keyboard_appcursor),
1390                       dlg_stdradiobutton_handler,
1391                       I(offsetof(Config, app_cursor)),
1392                       "Normal", I(0), "Application", I(1), NULL);
1393     ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1394                       HELPCTX(keyboard_appkeypad),
1395                       numeric_keypad_handler, P(NULL),
1396                       "Normal", I(0), "Application", I(1), "NetHack", I(2),
1397                       NULL);
1398
1399     /*
1400      * The Terminal/Bell panel.
1401      */
1402     ctrl_settitle(b, "Terminal/Bell",
1403                   "Options controlling the terminal bell");
1404
1405     s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1406     ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1407                       HELPCTX(bell_style),
1408                       dlg_stdradiobutton_handler, I(offsetof(Config, beep)),
1409                       "None (bell disabled)", I(BELL_DISABLED),
1410                       "Make default system alert sound", I(BELL_DEFAULT),
1411                       "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1412
1413     s = ctrl_getset(b, "Terminal/Bell", "overload",
1414                     "Control the bell overload behaviour");
1415     ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1416                   HELPCTX(bell_overload),
1417                   dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));
1418     ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1419                  HELPCTX(bell_overload),
1420                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));
1421     ctrl_editbox(s, "... in this many seconds", 't', 20,
1422                  HELPCTX(bell_overload),
1423                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
1424                  I(-TICKSPERSEC));
1425     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1426               HELPCTX(bell_overload));
1427     ctrl_editbox(s, "Seconds of silence required", 's', 20,
1428                  HELPCTX(bell_overload),
1429                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
1430                  I(-TICKSPERSEC));
1431
1432     /*
1433      * The Terminal/Features panel.
1434      */
1435     ctrl_settitle(b, "Terminal/Features",
1436                   "Enabling and disabling advanced terminal features");
1437
1438     s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1439     ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1440                   HELPCTX(features_application),
1441                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));
1442     ctrl_checkbox(s, "Disable application keypad mode", 'k',
1443                   HELPCTX(features_application),
1444                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));
1445     ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1446                   HELPCTX(features_mouse),
1447                   dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));
1448     ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1449                   HELPCTX(features_resize),
1450                   dlg_stdcheckbox_handler,
1451                   I(offsetof(Config,no_remote_resize)));
1452     ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1453                   HELPCTX(features_altscreen),
1454                   dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));
1455     ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1456                   HELPCTX(features_retitle),
1457                   dlg_stdcheckbox_handler,
1458                   I(offsetof(Config,no_remote_wintitle)));
1459     ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1460                       HELPCTX(features_qtitle),
1461                       dlg_stdradiobutton_handler,
1462                       I(offsetof(Config,remote_qtitle_action)),
1463                       "None", I(TITLE_NONE),
1464                       "Empty string", I(TITLE_EMPTY),
1465                       "Window title", I(TITLE_REAL), NULL);
1466     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1467                   HELPCTX(features_dbackspace),
1468                   dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
1469     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1470                   'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
1471                   I(offsetof(Config,no_remote_charset)));
1472     ctrl_checkbox(s, "Disable Arabic text shaping",
1473                   'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,
1474                   I(offsetof(Config, arabicshaping)));
1475     ctrl_checkbox(s, "Disable bidirectional text display",
1476                   'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,
1477                   I(offsetof(Config, bidi)));
1478
1479     /*
1480      * The Window panel.
1481      */
1482     str = dupprintf("Options controlling %s's window", appname);
1483     ctrl_settitle(b, "Window", str);
1484     sfree(str);
1485
1486     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1487     ctrl_columns(s, 2, 50, 50);
1488     c = ctrl_editbox(s, "Columns", 'm', 100,
1489                      HELPCTX(window_size),
1490                      dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
1491     c->generic.column = 0;
1492     c = ctrl_editbox(s, "Rows", 'r', 100,
1493                      HELPCTX(window_size),
1494                      dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
1495     c->generic.column = 1;
1496     ctrl_columns(s, 1, 100);
1497
1498     s = ctrl_getset(b, "Window", "scrollback",
1499                     "Control the scrollback in the window");
1500     ctrl_editbox(s, "Lines of scrollback", 's', 50,
1501                  HELPCTX(window_scrollback),
1502                  dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
1503     ctrl_checkbox(s, "Display scrollbar", 'd',
1504                   HELPCTX(window_scrollback),
1505                   dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
1506     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1507                   HELPCTX(window_scrollback),
1508                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
1509     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1510                   HELPCTX(window_scrollback),
1511                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
1512     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1513                   HELPCTX(window_erased),
1514                   dlg_stdcheckbox_handler,
1515                   I(offsetof(Config,erase_to_scrollback)));
1516
1517     /*
1518      * The Window/Appearance panel.
1519      */
1520     str = dupprintf("Configure the appearance of %s's window", appname);
1521     ctrl_settitle(b, "Window/Appearance", str);
1522     sfree(str);
1523
1524     s = ctrl_getset(b, "Window/Appearance", "cursor",
1525                     "Adjust the use of the cursor");
1526     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1527                       HELPCTX(appearance_cursor),
1528                       dlg_stdradiobutton_handler,
1529                       I(offsetof(Config, cursor_type)),
1530                       "Block", 'l', I(0),
1531                       "Underline", 'u', I(1),
1532                       "Vertical line", 'v', I(2), NULL);
1533     ctrl_checkbox(s, "Cursor blinks", 'b',
1534                   HELPCTX(appearance_cursor),
1535                   dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
1536
1537     s = ctrl_getset(b, "Window/Appearance", "font",
1538                     "Font settings");
1539     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1540                  HELPCTX(appearance_font),
1541                  dlg_stdfontsel_handler, I(offsetof(Config, font)));
1542
1543     s = ctrl_getset(b, "Window/Appearance", "mouse",
1544                     "Adjust the use of the mouse pointer");
1545     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1546                   HELPCTX(appearance_hidemouse),
1547                   dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
1548
1549     s = ctrl_getset(b, "Window/Appearance", "border",
1550                     "Adjust the window border");
1551     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1552                  HELPCTX(appearance_border),
1553                  dlg_stdeditbox_handler,
1554                  I(offsetof(Config,window_border)), I(-1));
1555
1556     /*
1557      * The Window/Behaviour panel.
1558      */
1559     str = dupprintf("Configure the behaviour of %s's window", appname);
1560     ctrl_settitle(b, "Window/Behaviour", str);
1561     sfree(str);
1562
1563     s = ctrl_getset(b, "Window/Behaviour", "title",
1564                     "Adjust the behaviour of the window title");
1565     ctrl_editbox(s, "Window title:", 't', 100,
1566                  HELPCTX(appearance_title),
1567                  dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
1568                  I(sizeof(((Config *)0)->wintitle)));
1569     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1570                   HELPCTX(appearance_title),
1571                   dlg_stdcheckbox_handler,
1572                   I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
1573
1574     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1575     ctrl_checkbox(s, "Warn before closing window", 'w',
1576                   HELPCTX(behaviour_closewarn),
1577                   dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
1578
1579     /*
1580      * The Window/Translation panel.
1581      */
1582     ctrl_settitle(b, "Window/Translation",
1583                   "Options controlling character set translation");
1584
1585     s = ctrl_getset(b, "Window/Translation", "trans",
1586                     "Character set translation");
1587     ctrl_combobox(s, "Remote character set:",
1588                   'r', 100, HELPCTX(translation_codepage),
1589                   codepage_handler, P(NULL), P(NULL));
1590
1591     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1592     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1593                   HELPCTX(translation_cjk_ambig_wide),
1594                   dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide)));
1595
1596     str = dupprintf("Adjust how %s handles line drawing characters", appname);
1597     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1598     sfree(str);
1599     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1600                       HELPCTX(translation_linedraw),
1601                       dlg_stdradiobutton_handler,
1602                       I(offsetof(Config, vtmode)),
1603                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1604                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1605                       NULL);
1606     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1607                   HELPCTX(selection_linedraw),
1608                   dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
1609
1610     /*
1611      * The Window/Selection panel.
1612      */
1613     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1614         
1615     s = ctrl_getset(b, "Window/Selection", "mouse",
1616                     "Control use of mouse");
1617     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1618                   HELPCTX(selection_shiftdrag),
1619                   dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
1620     ctrl_radiobuttons(s,
1621                       "Default selection mode (Alt+drag does the other one):",
1622                       NO_SHORTCUT, 2,
1623                       HELPCTX(selection_rect),
1624                       dlg_stdradiobutton_handler,
1625                       I(offsetof(Config, rect_select)),
1626                       "Normal", 'n', I(0),
1627                       "Rectangular block", 'r', I(1), NULL);
1628
1629     s = ctrl_getset(b, "Window/Selection", "charclass",
1630                     "Control the select-one-word-at-a-time mode");
1631     ccd = (struct charclass_data *)
1632         ctrl_alloc(b, sizeof(struct charclass_data));
1633     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1634                                 HELPCTX(selection_charclasses),
1635                                 charclass_handler, P(ccd));
1636     ccd->listbox->listbox.multisel = 1;
1637     ccd->listbox->listbox.ncols = 4;
1638     ccd->listbox->listbox.percentages = snewn(4, int);
1639     ccd->listbox->listbox.percentages[0] = 15;
1640     ccd->listbox->listbox.percentages[1] = 25;
1641     ccd->listbox->listbox.percentages[2] = 20;
1642     ccd->listbox->listbox.percentages[3] = 40;
1643     ctrl_columns(s, 2, 67, 33);
1644     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1645                                 HELPCTX(selection_charclasses),
1646                                 charclass_handler, P(ccd), P(NULL));
1647     ccd->editbox->generic.column = 0;
1648     ccd->button = ctrl_pushbutton(s, "Set", 's',
1649                                   HELPCTX(selection_charclasses),
1650                                   charclass_handler, P(ccd));
1651     ccd->button->generic.column = 1;
1652     ctrl_columns(s, 1, 100);
1653
1654     /*
1655      * The Window/Colours panel.
1656      */
1657     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1658
1659     s = ctrl_getset(b, "Window/Colours", "general",
1660                     "General options for colour usage");
1661     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1662                   HELPCTX(colours_ansi),
1663                   dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));
1664     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1665                   HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,
1666                   I(offsetof(Config,xterm_256_colour)));
1667     ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1668                   HELPCTX(colours_bold),
1669                   dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
1670
1671     str = dupprintf("Adjust the precise colours %s displays", appname);
1672     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1673     sfree(str);
1674     ctrl_text(s, "Select a colour from the list, and then click the"
1675               " Modify button to change its appearance.",
1676               HELPCTX(colours_config));
1677     ctrl_columns(s, 2, 67, 33);
1678     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1679     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1680                                HELPCTX(colours_config), colour_handler, P(cd));
1681     cd->listbox->generic.column = 0;
1682     cd->listbox->listbox.height = 7;
1683     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1684     c->generic.column = 1;
1685     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1686                              colour_handler, P(cd), P(NULL));
1687     cd->redit->generic.column = 1;
1688     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1689                              colour_handler, P(cd), P(NULL));
1690     cd->gedit->generic.column = 1;
1691     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1692                              colour_handler, P(cd), P(NULL));
1693     cd->bedit->generic.column = 1;
1694     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1695                                  colour_handler, P(cd));
1696     cd->button->generic.column = 1;
1697     ctrl_columns(s, 1, 100);
1698
1699     /*
1700      * The Connection panel. This doesn't show up if we're in a
1701      * non-network utility such as pterm. We tell this by being
1702      * passed a protocol < 0.
1703      */
1704     if (protocol >= 0) {
1705         ctrl_settitle(b, "Connection", "Options controlling the connection");
1706
1707         s = ctrl_getset(b, "Connection", "keepalive",
1708                         "Sending of null packets to keep session active");
1709         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1710                      HELPCTX(connection_keepalive),
1711                      dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
1712                      I(-1));
1713
1714         if (!midsession) {
1715             s = ctrl_getset(b, "Connection", "tcp",
1716                             "Low-level TCP connection options");
1717             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1718                           'n', HELPCTX(connection_nodelay),
1719                           dlg_stdcheckbox_handler,
1720                           I(offsetof(Config,tcp_nodelay)));
1721             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1722                           'p', HELPCTX(connection_tcpkeepalive),
1723                           dlg_stdcheckbox_handler,
1724                           I(offsetof(Config,tcp_keepalives)));
1725 #ifndef NO_IPV6
1726             s = ctrl_getset(b, "Connection", "ipversion",
1727                           "Internet protocol version");
1728             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1729                           HELPCTX(connection_ipversion),
1730                           dlg_stdradiobutton_handler,
1731                           I(offsetof(Config, addressfamily)),
1732                           "Auto", 'u', I(ADDRTYPE_UNSPEC),
1733                           "IPv4", '4', I(ADDRTYPE_IPV4),
1734                           "IPv6", '6', I(ADDRTYPE_IPV6),
1735                           NULL);
1736 #endif
1737
1738             {
1739                 char *label = backend_from_proto(PROT_SSH) ?
1740                     "Logical name of remote host (e.g. for SSH key lookup):" :
1741                     "Logical name of remote host:";
1742                 s = ctrl_getset(b, "Connection", "identity",
1743                                 "Logical name of remote host");
1744                 ctrl_editbox(s, label, 'm', 100,
1745                              HELPCTX(connection_loghost),
1746                              dlg_stdeditbox_handler, I(offsetof(Config,loghost)),
1747                              I(sizeof(((Config *)0)->loghost)));
1748             }
1749         }
1750
1751         /*
1752          * A sub-panel Connection/Data, containing options that
1753          * decide on data to send to the server.
1754          */
1755         if (!midsession) {
1756             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1757
1758             s = ctrl_getset(b, "Connection/Data", "login",
1759                             "Login details");
1760             ctrl_editbox(s, "Auto-login username", 'u', 50,
1761                          HELPCTX(connection_username),
1762                          dlg_stdeditbox_handler, I(offsetof(Config,username)),
1763                          I(sizeof(((Config *)0)->username)));
1764             {
1765                 /* We assume the local username is sufficiently stable
1766                  * to include on the dialog box. */
1767                 char *user = get_username();
1768                 char *userlabel = dupprintf("Use system username (%s)",
1769                                             user ? user : "");
1770                 sfree(user);
1771                 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1772                                   HELPCTX(connection_username_from_env),
1773                                   dlg_stdradiobutton_handler,
1774                                   I(offsetof(Config, username_from_env)),
1775                                   "Prompt", I(FALSE),
1776                                   userlabel, I(TRUE),
1777                                   NULL);
1778                 sfree(userlabel);
1779             }
1780
1781             s = ctrl_getset(b, "Connection/Data", "term",
1782                             "Terminal details");
1783             ctrl_editbox(s, "Terminal-type string", 't', 50,
1784                          HELPCTX(connection_termtype),
1785                          dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
1786                          I(sizeof(((Config *)0)->termtype)));
1787             ctrl_editbox(s, "Terminal speeds", 's', 50,
1788                          HELPCTX(connection_termspeed),
1789                          dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1790                          I(sizeof(((Config *)0)->termspeed)));
1791
1792             s = ctrl_getset(b, "Connection/Data", "env",
1793                             "Environment variables");
1794             ctrl_columns(s, 2, 80, 20);
1795             ed = (struct environ_data *)
1796                 ctrl_alloc(b, sizeof(struct environ_data));
1797             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1798                                       HELPCTX(telnet_environ),
1799                                       environ_handler, P(ed), P(NULL));
1800             ed->varbox->generic.column = 0;
1801             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1802                                       HELPCTX(telnet_environ),
1803                                       environ_handler, P(ed), P(NULL));
1804             ed->valbox->generic.column = 0;
1805             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1806                                             HELPCTX(telnet_environ),
1807                                             environ_handler, P(ed));
1808             ed->addbutton->generic.column = 1;
1809             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1810                                             HELPCTX(telnet_environ),
1811                                             environ_handler, P(ed));
1812             ed->rembutton->generic.column = 1;
1813             ctrl_columns(s, 1, 100);
1814             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1815                                        HELPCTX(telnet_environ),
1816                                        environ_handler, P(ed));
1817             ed->listbox->listbox.height = 3;
1818             ed->listbox->listbox.ncols = 2;
1819             ed->listbox->listbox.percentages = snewn(2, int);
1820             ed->listbox->listbox.percentages[0] = 30;
1821             ed->listbox->listbox.percentages[1] = 70;
1822         }
1823
1824     }
1825
1826     if (!midsession) {
1827         /*
1828          * The Connection/Proxy panel.
1829          */
1830         ctrl_settitle(b, "Connection/Proxy",
1831                       "Options controlling proxy usage");
1832
1833         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1834         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1835                           HELPCTX(proxy_type),
1836                           dlg_stdradiobutton_handler,
1837                           I(offsetof(Config, proxy_type)),
1838                           "None", I(PROXY_NONE),
1839                           "SOCKS 4", I(PROXY_SOCKS4),
1840                           "SOCKS 5", I(PROXY_SOCKS5),
1841                           "HTTP", I(PROXY_HTTP),
1842                           "Telnet", I(PROXY_TELNET),
1843                           NULL);
1844         ctrl_columns(s, 2, 80, 20);
1845         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1846                          HELPCTX(proxy_main),
1847                          dlg_stdeditbox_handler,
1848                          I(offsetof(Config,proxy_host)),
1849                          I(sizeof(((Config *)0)->proxy_host)));
1850         c->generic.column = 0;
1851         c = ctrl_editbox(s, "Port", 'p', 100,
1852                          HELPCTX(proxy_main),
1853                          dlg_stdeditbox_handler,
1854                          I(offsetof(Config,proxy_port)),
1855                          I(-1));
1856         c->generic.column = 1;
1857         ctrl_columns(s, 1, 100);
1858         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1859                      HELPCTX(proxy_exclude),
1860                      dlg_stdeditbox_handler,
1861                      I(offsetof(Config,proxy_exclude_list)),
1862                      I(sizeof(((Config *)0)->proxy_exclude_list)));
1863         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1864                       HELPCTX(proxy_exclude),
1865                       dlg_stdcheckbox_handler,
1866                       I(offsetof(Config,even_proxy_localhost)));
1867         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1868                           HELPCTX(proxy_dns),
1869                           dlg_stdradiobutton_handler,
1870                           I(offsetof(Config, proxy_dns)),
1871                           "No", I(FORCE_OFF),
1872                           "Auto", I(AUTO),
1873                           "Yes", I(FORCE_ON), NULL);
1874         ctrl_editbox(s, "Username", 'u', 60,
1875                      HELPCTX(proxy_auth),
1876                      dlg_stdeditbox_handler,
1877                      I(offsetof(Config,proxy_username)),
1878                      I(sizeof(((Config *)0)->proxy_username)));
1879         c = ctrl_editbox(s, "Password", 'w', 60,
1880                          HELPCTX(proxy_auth),
1881                          dlg_stdeditbox_handler,
1882                          I(offsetof(Config,proxy_password)),
1883                          I(sizeof(((Config *)0)->proxy_password)));
1884         c->editbox.password = 1;
1885         ctrl_editbox(s, "Telnet command", 'm', 100,
1886                      HELPCTX(proxy_command),
1887                      dlg_stdeditbox_handler,
1888                      I(offsetof(Config,proxy_telnet_command)),
1889                      I(sizeof(((Config *)0)->proxy_telnet_command)));
1890     }
1891
1892     /*
1893      * The Telnet panel exists in the base config box, and in a
1894      * mid-session reconfig box _if_ we're using Telnet.
1895      */
1896     if (!midsession || protocol == PROT_TELNET) {
1897         /*
1898          * The Connection/Telnet panel.
1899          */
1900         ctrl_settitle(b, "Connection/Telnet",
1901                       "Options controlling Telnet connections");
1902
1903         s = ctrl_getset(b, "Connection/Telnet", "protocol",
1904                         "Telnet protocol adjustments");
1905
1906         if (!midsession) {
1907             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
1908                               NO_SHORTCUT, 2,
1909                               HELPCTX(telnet_oldenviron),
1910                               dlg_stdradiobutton_handler,
1911                               I(offsetof(Config, rfc_environ)),
1912                               "BSD (commonplace)", 'b', I(0),
1913                               "RFC 1408 (unusual)", 'f', I(1), NULL);
1914             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
1915                               HELPCTX(telnet_passive),
1916                               dlg_stdradiobutton_handler,
1917                               I(offsetof(Config, passive_telnet)),
1918                               "Passive", I(1), "Active", I(0), NULL);
1919         }
1920         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
1921                       HELPCTX(telnet_specialkeys),
1922                       dlg_stdcheckbox_handler,
1923                       I(offsetof(Config,telnet_keyboard)));
1924         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
1925                       'm', HELPCTX(telnet_newline),
1926                       dlg_stdcheckbox_handler,
1927                       I(offsetof(Config,telnet_newline)));
1928     }
1929
1930     if (!midsession) {
1931
1932         /*
1933          * The Connection/Rlogin panel.
1934          */
1935         ctrl_settitle(b, "Connection/Rlogin",
1936                       "Options controlling Rlogin connections");
1937
1938         s = ctrl_getset(b, "Connection/Rlogin", "data",
1939                         "Data to send to the server");
1940         ctrl_editbox(s, "Local username:", 'l', 50,
1941                      HELPCTX(rlogin_localuser),
1942                      dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
1943                      I(sizeof(((Config *)0)->localusername)));
1944
1945     }
1946
1947     /*
1948      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
1949      * when we're not doing SSH.
1950      */
1951
1952     if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
1953
1954         /*
1955          * The Connection/SSH panel.
1956          */
1957         ctrl_settitle(b, "Connection/SSH",
1958                       "Options controlling SSH connections");
1959
1960         if (midsession && protcfginfo == 1) {
1961             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
1962             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
1963                       "session; it is only here so that sub-panels of it can "
1964                       "exist without looking strange.", HELPCTX(no_help));
1965         }
1966
1967         if (!midsession) {
1968
1969             s = ctrl_getset(b, "Connection/SSH", "data",
1970                             "Data to send to the server");
1971             ctrl_editbox(s, "Remote command:", 'r', 100,
1972                          HELPCTX(ssh_command),
1973                          dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
1974                          I(sizeof(((Config *)0)->remote_cmd)));
1975
1976             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1977             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
1978                           HELPCTX(ssh_noshell),
1979                           dlg_stdcheckbox_handler,
1980                           I(offsetof(Config,ssh_no_shell)));
1981         }
1982
1983         if (!midsession || protcfginfo != 1) {
1984             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1985
1986             ctrl_checkbox(s, "Enable compression", 'e',
1987                           HELPCTX(ssh_compress),
1988                           dlg_stdcheckbox_handler,
1989                           I(offsetof(Config,compression)));
1990         }
1991
1992         if (!midsession) {
1993             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1994
1995             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
1996                               HELPCTX(ssh_protocol),
1997                               dlg_stdradiobutton_handler,
1998                               I(offsetof(Config, sshprot)),
1999                               "1 only", 'l', I(0),
2000                               "1", '1', I(1),
2001                               "2", '2', I(2),
2002                               "2 only", 'y', I(3), NULL);
2003         }
2004
2005         if (!midsession || protcfginfo != 1) {
2006             s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
2007             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2008                               HELPCTX(ssh_ciphers),
2009                               cipherlist_handler, P(NULL));
2010             c->listbox.height = 6;
2011
2012             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2013                           HELPCTX(ssh_ciphers),
2014                           dlg_stdcheckbox_handler,
2015                           I(offsetof(Config,ssh2_des_cbc)));
2016         }
2017
2018         /*
2019          * The Connection/SSH/Kex panel. (Owing to repeat key
2020          * exchange, this is all meaningful in mid-session _if_
2021          * we're using SSH-2 or haven't decided yet.)
2022          */
2023         if (protcfginfo != 1) {
2024             ctrl_settitle(b, "Connection/SSH/Kex",
2025                           "Options controlling SSH key exchange");
2026
2027             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2028                             "Key exchange algorithm options");
2029             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2030                               HELPCTX(ssh_kexlist),
2031                               kexlist_handler, P(NULL));
2032             c->listbox.height = 5;
2033
2034             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2035                             "Options controlling key re-exchange");
2036
2037             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2038                          HELPCTX(ssh_kex_repeat),
2039                          dlg_stdeditbox_handler,
2040                          I(offsetof(Config,ssh_rekey_time)),
2041                          I(-1));
2042             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2043                          HELPCTX(ssh_kex_repeat),
2044                          dlg_stdeditbox_handler,
2045                          I(offsetof(Config,ssh_rekey_data)),
2046                          I(16));
2047             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2048                       HELPCTX(ssh_kex_repeat));
2049         }
2050
2051         if (!midsession) {
2052
2053             /*
2054              * The Connection/SSH/Auth panel.
2055              */
2056             ctrl_settitle(b, "Connection/SSH/Auth",
2057                           "Options controlling SSH authentication");
2058
2059             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2060             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2061                           HELPCTX(ssh_auth_bypass),
2062                           dlg_stdcheckbox_handler,
2063                           I(offsetof(Config,ssh_no_userauth)));
2064
2065             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2066                             "Authentication methods");
2067             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2068                           HELPCTX(ssh_auth_pageant),
2069                           dlg_stdcheckbox_handler,
2070                           I(offsetof(Config,tryagent)));
2071             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2072                           HELPCTX(ssh_auth_tis),
2073                           dlg_stdcheckbox_handler,
2074                           I(offsetof(Config,try_tis_auth)));
2075             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2076                           'i', HELPCTX(ssh_auth_ki),
2077                           dlg_stdcheckbox_handler,
2078                           I(offsetof(Config,try_ki_auth)));
2079
2080 #ifndef NO_GSSAPI
2081             ctrl_checkbox(s, "Attempt GSSAPI auth (SSH-2)",
2082                           NO_SHORTCUT, HELPCTX(no_help),
2083                           dlg_stdcheckbox_handler,
2084                           I(offsetof(Config,try_gssapi_auth)));
2085 #endif
2086
2087             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2088                             "Authentication parameters");
2089             ctrl_checkbox(s, "Allow agent forwarding", 'f',
2090                           HELPCTX(ssh_auth_agentfwd),
2091                           dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
2092             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", 'u',
2093                           HELPCTX(ssh_auth_changeuser),
2094                           dlg_stdcheckbox_handler,
2095                           I(offsetof(Config,change_username)));
2096 #ifndef NO_GSSAPI
2097             ctrl_checkbox(s, "Allow GSSAPI credential delegation in SSH-2", NO_SHORTCUT,
2098                           HELPCTX(no_help),
2099                           dlg_stdcheckbox_handler,
2100                           I(offsetof(Config,gssapifwd)));
2101 #endif
2102             ctrl_filesel(s, "Private key file for authentication:", 'k',
2103                          FILTER_KEY_FILES, FALSE, "Select private key file",
2104                          HELPCTX(ssh_auth_privkey),
2105                          dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
2106         }
2107
2108         if (!midsession) {
2109             /*
2110              * The Connection/SSH/TTY panel.
2111              */
2112             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2113
2114             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2115             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2116                           HELPCTX(ssh_nopty),
2117                           dlg_stdcheckbox_handler,
2118                           I(offsetof(Config,nopty)));
2119
2120             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2121                             "Terminal modes");
2122             td = (struct ttymodes_data *)
2123                 ctrl_alloc(b, sizeof(struct ttymodes_data));
2124             ctrl_columns(s, 2, 75, 25);
2125             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2126             c->generic.column = 0;
2127             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2128                                             HELPCTX(ssh_ttymodes),
2129                                             ttymodes_handler, P(td));
2130             td->rembutton->generic.column = 1;
2131             td->rembutton->generic.tabdelay = 1;
2132             ctrl_columns(s, 1, 100);
2133             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2134                                        HELPCTX(ssh_ttymodes),
2135                                        ttymodes_handler, P(td));
2136             td->listbox->listbox.multisel = 1;
2137             td->listbox->listbox.height = 4;
2138             td->listbox->listbox.ncols = 2;
2139             td->listbox->listbox.percentages = snewn(2, int);
2140             td->listbox->listbox.percentages[0] = 40;
2141             td->listbox->listbox.percentages[1] = 60;
2142             ctrl_tabdelay(s, td->rembutton);
2143             ctrl_columns(s, 2, 75, 25);
2144             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2145                                          HELPCTX(ssh_ttymodes),
2146                                          ttymodes_handler, P(td));
2147             td->modelist->generic.column = 0;
2148             td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2149                                             HELPCTX(ssh_ttymodes),
2150                                             ttymodes_handler, P(td));
2151             td->addbutton->generic.column = 1;
2152             td->addbutton->generic.tabdelay = 1;
2153             ctrl_columns(s, 1, 100);        /* column break */
2154             /* Bit of a hack to get the value radio buttons and
2155              * edit-box on the same row. */
2156             ctrl_columns(s, 3, 25, 50, 25);
2157             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2158             c->generic.column = 0;
2159             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2160                                              HELPCTX(ssh_ttymodes),
2161                                              ttymodes_handler, P(td),
2162                                              "Auto", NO_SHORTCUT, P(NULL),
2163                                              "This:", NO_SHORTCUT, P(NULL),
2164                                              NULL);
2165             td->valradio->generic.column = 1;
2166             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2167                                       HELPCTX(ssh_ttymodes),
2168                                       ttymodes_handler, P(td), P(NULL));
2169             td->valbox->generic.column = 2;
2170             ctrl_tabdelay(s, td->addbutton);
2171
2172         }
2173
2174         if (!midsession) {
2175             /*
2176              * The Connection/SSH/X11 panel.
2177              */
2178             ctrl_settitle(b, "Connection/SSH/X11",
2179                           "Options controlling SSH X11 forwarding");
2180
2181             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2182             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2183                           HELPCTX(ssh_tunnels_x11),
2184                           dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
2185             ctrl_editbox(s, "X display location", 'x', 50,
2186                          HELPCTX(ssh_tunnels_x11),
2187                          dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
2188                          I(sizeof(((Config *)0)->x11_display)));
2189             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2190                               HELPCTX(ssh_tunnels_x11auth),
2191                               dlg_stdradiobutton_handler,
2192                               I(offsetof(Config, x11_auth)),
2193                               "MIT-Magic-Cookie-1", I(X11_MIT),
2194                               "XDM-Authorization-1", I(X11_XDM), NULL);
2195         }
2196
2197         /*
2198          * The Tunnels panel _is_ still available in mid-session.
2199          */
2200         ctrl_settitle(b, "Connection/SSH/Tunnels",
2201                       "Options controlling SSH port forwarding");
2202
2203         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2204                         "Port forwarding");
2205         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2206                       HELPCTX(ssh_tunnels_portfwd_localhost),
2207                       dlg_stdcheckbox_handler,
2208                       I(offsetof(Config,lport_acceptall)));
2209         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2210                       HELPCTX(ssh_tunnels_portfwd_localhost),
2211                       dlg_stdcheckbox_handler,
2212                       I(offsetof(Config,rport_acceptall)));
2213
2214         ctrl_columns(s, 3, 55, 20, 25);
2215         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2216         c->generic.column = COLUMN_FIELD(0,2);
2217         /* You want to select from the list, _then_ hit Remove. So tab order
2218          * should be that way round. */
2219         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2220         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2221                                          HELPCTX(ssh_tunnels_portfwd),
2222                                          portfwd_handler, P(pfd));
2223         pfd->rembutton->generic.column = 2;
2224         pfd->rembutton->generic.tabdelay = 1;
2225         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2226                                     HELPCTX(ssh_tunnels_portfwd),
2227                                     portfwd_handler, P(pfd));
2228         pfd->listbox->listbox.height = 3;
2229         pfd->listbox->listbox.ncols = 2;
2230         pfd->listbox->listbox.percentages = snewn(2, int);
2231         pfd->listbox->listbox.percentages[0] = 20;
2232         pfd->listbox->listbox.percentages[1] = 80;
2233         ctrl_tabdelay(s, pfd->rembutton);
2234         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2235         /* You want to enter source, destination and type, _then_ hit Add.
2236          * Again, we adjust the tab order to reflect this. */
2237         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2238                                          HELPCTX(ssh_tunnels_portfwd),
2239                                          portfwd_handler, P(pfd));
2240         pfd->addbutton->generic.column = 2;
2241         pfd->addbutton->generic.tabdelay = 1;
2242         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2243                                       HELPCTX(ssh_tunnels_portfwd),
2244                                       portfwd_handler, P(pfd), P(NULL));
2245         pfd->sourcebox->generic.column = 0;
2246         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2247                                     HELPCTX(ssh_tunnels_portfwd),
2248                                     portfwd_handler, P(pfd), P(NULL));
2249         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2250                                            HELPCTX(ssh_tunnels_portfwd),
2251                                            portfwd_handler, P(pfd),
2252                                            "Local", 'l', P(NULL),
2253                                            "Remote", 'm', P(NULL),
2254                                            "Dynamic", 'y', P(NULL),
2255                                            NULL);
2256 #ifndef NO_IPV6
2257         pfd->addressfamily =
2258             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2259                               HELPCTX(ssh_tunnels_portfwd_ipversion),
2260                               portfwd_handler, P(pfd),
2261                               "Auto", 'u', I(ADDRTYPE_UNSPEC),
2262                               "IPv4", '4', I(ADDRTYPE_IPV4),
2263                               "IPv6", '6', I(ADDRTYPE_IPV6),
2264                               NULL);
2265 #endif
2266         ctrl_tabdelay(s, pfd->addbutton);
2267         ctrl_columns(s, 1, 100);
2268
2269         if (!midsession) {
2270             /*
2271              * The Connection/SSH/Bugs panel.
2272              */
2273             ctrl_settitle(b, "Connection/SSH/Bugs",
2274                           "Workarounds for SSH server bugs");
2275
2276             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2277                             "Detection of known bugs in SSH servers");
2278             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2279                           HELPCTX(ssh_bugs_ignore1),
2280                           sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
2281             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2282                           HELPCTX(ssh_bugs_plainpw1),
2283                           sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
2284             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2285                           HELPCTX(ssh_bugs_rsa1),
2286                           sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
2287             ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2288                           HELPCTX(ssh_bugs_ignore2),
2289                           sshbug_handler, I(offsetof(Config,sshbug_ignore2)));
2290             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2291                           HELPCTX(ssh_bugs_hmac2),
2292                           sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
2293             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2294                           HELPCTX(ssh_bugs_derivekey2),
2295                           sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
2296             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2297                           HELPCTX(ssh_bugs_rsapad2),
2298                           sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
2299             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2300                           HELPCTX(ssh_bugs_pksessid2),
2301                           sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
2302             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2303                           HELPCTX(ssh_bugs_rekey2),
2304                           sshbug_handler, I(offsetof(Config,sshbug_rekey2)));
2305             ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2306                           HELPCTX(ssh_bugs_maxpkt2),
2307                           sshbug_handler, I(offsetof(Config,sshbug_maxpkt2)));
2308         }
2309     }
2310 }