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