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