]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - config.c
Allow reconfiguration of compression and cipher settings in
[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     union control *addressfamily;
670 };
671
672 static void portfwd_handler(union control *ctrl, void *dlg,
673                             void *data, int event)
674 {
675     Config *cfg = (Config *)data;
676     struct portfwd_data *pfd =
677         (struct portfwd_data *)ctrl->generic.context.p;
678
679     if (event == EVENT_REFRESH) {
680         if (ctrl == pfd->listbox) {
681             char *p = cfg->portfwd;
682             dlg_update_start(ctrl, dlg);
683             dlg_listbox_clear(ctrl, dlg);
684             while (*p) {
685                 dlg_listbox_add(ctrl, dlg, p);
686                 p += strlen(p) + 1;
687             }
688             dlg_update_done(ctrl, dlg);
689         } else if (ctrl == pfd->direction) {
690             /*
691              * Default is Local.
692              */
693             dlg_radiobutton_set(ctrl, dlg, 0);
694          } else if (ctrl == pfd->addressfamily) {
695             dlg_radiobutton_set(ctrl, dlg, 0);
696         }
697     } else if (event == EVENT_ACTION) {
698         if (ctrl == pfd->addbutton) {
699             char str[sizeof(cfg->portfwd)];
700             char *p;
701             int i, type;
702             int whichbutton;
703
704             i = 0;
705             whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
706             if (whichbutton == 1)
707                 str[i++] = '4';
708             else if (whichbutton == 2)
709                 str[i++] = '6';
710
711             whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
712             if (whichbutton == 0)
713                 type = 'L';
714             else if (whichbutton == 1)
715                 type = 'R';
716             else
717                 type = 'D';
718             str[i++] = type;
719
720             dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i);
721             if (!str[2]) {
722                 dlg_error_msg(dlg, "You need to specify a source port number");
723                 return;
724             }
725             p = str + strlen(str);
726             if (type != 'D') {
727                 *p++ = '\t';
728                 dlg_editbox_get(pfd->destbox, dlg, p,
729                                 sizeof(str)-1 - (p - str));
730                 if (!*p || !strchr(p, ':')) {
731                     dlg_error_msg(dlg,
732                                   "You need to specify a destination address\n"
733                                   "in the form \"host.name:port\"");
734                     return;
735                 }
736             } else
737                 *p = '\0';
738             p = cfg->portfwd;
739             while (*p) {
740                 while (*p)
741                     p++;
742                 p++;
743             }
744             if ((p - cfg->portfwd) + strlen(str) + 2 <
745                 sizeof(cfg->portfwd)) {
746                 strcpy(p, str);
747                 p[strlen(str) + 1] = '\0';
748                 dlg_listbox_add(pfd->listbox, dlg, str);
749                 dlg_editbox_set(pfd->sourcebox, dlg, "");
750                 dlg_editbox_set(pfd->destbox, dlg, "");
751             } else {
752                 dlg_error_msg(dlg, "Too many forwardings");
753             }
754         } else if (ctrl == pfd->rembutton) {
755             int i = dlg_listbox_index(pfd->listbox, dlg);
756             if (i < 0)
757                 dlg_beep(dlg);
758             else {
759                 char *p, *q;
760
761                 dlg_listbox_del(pfd->listbox, dlg, i);
762                 p = cfg->portfwd;
763                 while (i > 0) {
764                     if (!*p)
765                         goto disaster2;
766                     while (*p)
767                         p++;
768                     p++;
769                     i--;
770                 }
771                 q = p;
772                 if (!*p)
773                     goto disaster2;
774                 while (*p)
775                     p++;
776                 p++;
777                 while (*p) {
778                     while (*p)
779                         *q++ = *p++;
780                     *q++ = *p++;
781                 }
782                 *q = '\0';
783                 disaster2:;
784             }
785         }
786     }
787 }
788
789 void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
790                       int midsession, int protocol, int protcfginfo)
791 {
792     struct controlset *s;
793     struct sessionsaver_data *ssd;
794     struct charclass_data *ccd;
795     struct colour_data *cd;
796     struct environ_data *ed;
797     struct portfwd_data *pfd;
798     union control *c;
799     char *str;
800
801     ssd = (struct sessionsaver_data *)
802         ctrl_alloc(b, sizeof(struct sessionsaver_data));
803     memset(ssd, 0, sizeof(*ssd));
804     ssd->midsession = midsession;
805
806     /*
807      * The standard panel that appears at the bottom of all panels:
808      * Open, Cancel, Apply etc.
809      */
810     s = ctrl_getset(b, "", "", "");
811     ctrl_columns(s, 5, 20, 20, 20, 20, 20);
812     ssd->okbutton = ctrl_pushbutton(s,
813                                     (midsession ? "Apply" : "Open"),
814                                     (char)(midsession ? 'a' : 'o'),
815                                     HELPCTX(no_help),
816                                     sessionsaver_handler, P(ssd));
817     ssd->okbutton->button.isdefault = TRUE;
818     ssd->okbutton->generic.column = 3;
819     ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
820                                         sessionsaver_handler, P(ssd));
821     ssd->cancelbutton->button.iscancel = TRUE;
822     ssd->cancelbutton->generic.column = 4;
823     /* We carefully don't close the 5-column part, so that platform-
824      * specific add-ons can put extra buttons alongside Open and Cancel. */
825
826     /*
827      * The Session panel.
828      */
829     str = dupprintf("Basic options for your %s session", appname);
830     ctrl_settitle(b, "Session", str);
831     sfree(str);
832
833     if (!midsession) {
834         s = ctrl_getset(b, "Session", "hostport",
835                         "Specify your connection by host name or IP address");
836         ctrl_columns(s, 2, 75, 25);
837         c = ctrl_editbox(s, "Host Name (or IP address)", 'n', 100,
838                          HELPCTX(session_hostname),
839                          dlg_stdeditbox_handler, I(offsetof(Config,host)),
840                          I(sizeof(((Config *)0)->host)));
841         c->generic.column = 0;
842         c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(session_hostname),
843                          dlg_stdeditbox_handler,
844                          I(offsetof(Config,port)), I(-1));
845         c->generic.column = 1;
846         ctrl_columns(s, 1, 100);
847         if (backends[3].name == NULL) {
848             ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 3,
849                               HELPCTX(session_hostname),
850                               protocolbuttons_handler, P(c),
851                               "Raw", 'r', I(PROT_RAW),
852                               "Telnet", 't', I(PROT_TELNET),
853                               "Rlogin", 'i', I(PROT_RLOGIN),
854                               NULL);
855         } else {
856             ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 4,
857                               HELPCTX(session_hostname),
858                               protocolbuttons_handler, P(c),
859                               "Raw", 'r', I(PROT_RAW),
860                               "Telnet", 't', I(PROT_TELNET),
861                               "Rlogin", 'i', I(PROT_RLOGIN),
862                               "SSH", 's', I(PROT_SSH),
863                               NULL);
864         }
865     }
866
867     /*
868      * The Load/Save panel is available even in mid-session.
869      */
870     s = ctrl_getset(b, "Session", "savedsessions",
871                     "Load, save or delete a stored session");
872     ctrl_columns(s, 2, 75, 25);
873     ssd->sesslist = sesslist;
874     ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
875                                 HELPCTX(session_saved),
876                                 sessionsaver_handler, P(ssd), P(NULL));
877     ssd->editbox->generic.column = 0;
878     /* Reset columns so that the buttons are alongside the list, rather
879      * than alongside that edit box. */
880     ctrl_columns(s, 1, 100);
881     ctrl_columns(s, 2, 75, 25);
882     ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
883                                 HELPCTX(session_saved),
884                                 sessionsaver_handler, P(ssd));
885     ssd->listbox->generic.column = 0;
886     ssd->listbox->listbox.height = 7;
887     if (!midsession) {
888         ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
889                                           HELPCTX(session_saved),
890                                           sessionsaver_handler, P(ssd));
891         ssd->loadbutton->generic.column = 1;
892     } else {
893         /* We can't offer the Load button mid-session, as it would allow the
894          * user to load and subsequently save settings they can't see. (And
895          * also change otherwise immutable settings underfoot; that probably
896          * shouldn't be a problem, but.) */
897         ssd->loadbutton = NULL;
898     }
899     /* "Save" button is permitted mid-session. */
900     ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
901                                       HELPCTX(session_saved),
902                                       sessionsaver_handler, P(ssd));
903     ssd->savebutton->generic.column = 1;
904     if (!midsession) {
905         ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
906                                          HELPCTX(session_saved),
907                                          sessionsaver_handler, P(ssd));
908         ssd->delbutton->generic.column = 1;
909     } else {
910         /* Disable the Delete button mid-session too, for UI consistency. */
911         ssd->delbutton = NULL;
912     }
913     ctrl_columns(s, 1, 100);
914
915     s = ctrl_getset(b, "Session", "otheropts", NULL);
916     c = ctrl_radiobuttons(s, "Close window on exit:", 'w', 4,
917                           HELPCTX(session_coe),
918                           dlg_stdradiobutton_handler,
919                           I(offsetof(Config, close_on_exit)),
920                           "Always", I(FORCE_ON),
921                           "Never", I(FORCE_OFF),
922                           "Only on clean exit", I(AUTO), NULL);
923
924     /*
925      * The Session/Logging panel.
926      */
927     ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
928
929     s = ctrl_getset(b, "Session/Logging", "main", NULL);
930     /*
931      * The logging buttons change depending on whether SSH packet
932      * logging can sensibly be available.
933      */
934     {
935         char *sshlogname;
936         if ((midsession && protocol == PROT_SSH) ||
937             (!midsession && backends[3].name != NULL))
938             sshlogname = "Log SSH packet data";
939         else
940             sshlogname = NULL;         /* this will disable the button */
941         ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 1,
942                           HELPCTX(logging_main),
943                           dlg_stdradiobutton_handler,
944                           I(offsetof(Config, logtype)),
945                           "Logging turned off completely", 't', I(LGTYP_NONE),
946                           "Log printable output only", 'p', I(LGTYP_ASCII),
947                           "Log all session output", 'l', I(LGTYP_DEBUG),
948                           sshlogname, 's', I(LGTYP_PACKETS),
949                           NULL);
950     }
951     ctrl_filesel(s, "Log file name:", 'f',
952                  NULL, TRUE, "Select session log file name",
953                  HELPCTX(logging_filename),
954                  dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));
955     ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
956               " &T for time, and &H for host name)",
957               HELPCTX(logging_filename));
958     ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
959                       HELPCTX(logging_exists),
960                       dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),
961                       "Always overwrite it", I(LGXF_OVR),
962                       "Always append to the end of it", I(LGXF_APN),
963                       "Ask the user every time", I(LGXF_ASK), NULL);
964     ctrl_checkbox(s, "Flush log file frequently", 'u',
965                  HELPCTX(logging_flush),
966                  dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));
967
968     if ((midsession && protocol == PROT_SSH) ||
969         (!midsession && backends[3].name != NULL)) {
970         s = ctrl_getset(b, "Session/Logging", "ssh",
971                         "Options specific to SSH packet logging");
972         ctrl_checkbox(s, "Omit known password fields", 'k',
973                       HELPCTX(logging_ssh_omit_password),
974                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass)));
975         ctrl_checkbox(s, "Omit session data", 'd',
976                       HELPCTX(logging_ssh_omit_data),
977                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata)));
978     }
979
980     /*
981      * The Terminal panel.
982      */
983     ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
984
985     s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
986     ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
987                   HELPCTX(terminal_autowrap),
988                   dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));
989     ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
990                   HELPCTX(terminal_decom),
991                   dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));
992     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
993                   HELPCTX(terminal_lfhascr),
994                   dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
995     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
996                   HELPCTX(terminal_bce),
997                   dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
998     ctrl_checkbox(s, "Enable blinking text", 'n',
999                   HELPCTX(terminal_blink),
1000                   dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));
1001     ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1002                  HELPCTX(terminal_answerback),
1003                  dlg_stdeditbox_handler, I(offsetof(Config,answerback)),
1004                  I(sizeof(((Config *)0)->answerback)));
1005
1006     s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1007     ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1008                       HELPCTX(terminal_localecho),
1009                       dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),
1010                       "Auto", I(AUTO),
1011                       "Force on", I(FORCE_ON),
1012                       "Force off", I(FORCE_OFF), NULL);
1013     ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1014                       HELPCTX(terminal_localedit),
1015                       dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),
1016                       "Auto", I(AUTO),
1017                       "Force on", I(FORCE_ON),
1018                       "Force off", I(FORCE_OFF), NULL);
1019
1020     s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1021     ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1022                   HELPCTX(terminal_printing),
1023                   printerbox_handler, P(NULL), P(NULL));
1024
1025     /*
1026      * The Terminal/Keyboard panel.
1027      */
1028     ctrl_settitle(b, "Terminal/Keyboard",
1029                   "Options controlling the effects of keys");
1030
1031     s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1032                     "Change the sequences sent by:");
1033     ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1034                       HELPCTX(keyboard_backspace),
1035                       dlg_stdradiobutton_handler,
1036                       I(offsetof(Config, bksp_is_delete)),
1037                       "Control-H", I(0), "Control-? (127)", I(1), NULL);
1038     ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1039                       HELPCTX(keyboard_homeend),
1040                       dlg_stdradiobutton_handler,
1041                       I(offsetof(Config, rxvt_homeend)),
1042                       "Standard", I(0), "rxvt", I(1), NULL);
1043     ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1044                       HELPCTX(keyboard_funkeys),
1045                       dlg_stdradiobutton_handler,
1046                       I(offsetof(Config, funky_type)),
1047                       "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1048                       "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1049
1050     s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1051                     "Application keypad settings:");
1052     ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1053                       HELPCTX(keyboard_appcursor),
1054                       dlg_stdradiobutton_handler,
1055                       I(offsetof(Config, app_cursor)),
1056                       "Normal", I(0), "Application", I(1), NULL);
1057     ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1058                       HELPCTX(keyboard_appkeypad),
1059                       numeric_keypad_handler, P(NULL),
1060                       "Normal", I(0), "Application", I(1), "NetHack", I(2),
1061                       NULL);
1062
1063     /*
1064      * The Terminal/Bell panel.
1065      */
1066     ctrl_settitle(b, "Terminal/Bell",
1067                   "Options controlling the terminal bell");
1068
1069     s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1070     ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1071                       HELPCTX(bell_style),
1072                       dlg_stdradiobutton_handler, I(offsetof(Config, beep)),
1073                       "None (bell disabled)", I(BELL_DISABLED),
1074                       "Make default system alert sound", I(BELL_DEFAULT),
1075                       "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1076
1077     s = ctrl_getset(b, "Terminal/Bell", "overload",
1078                     "Control the bell overload behaviour");
1079     ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1080                   HELPCTX(bell_overload),
1081                   dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));
1082     ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1083                  HELPCTX(bell_overload),
1084                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));
1085     ctrl_editbox(s, "... in this many seconds", 't', 20,
1086                  HELPCTX(bell_overload),
1087                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
1088                  I(-TICKSPERSEC));
1089     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1090               HELPCTX(bell_overload));
1091     ctrl_editbox(s, "Seconds of silence required", 's', 20,
1092                  HELPCTX(bell_overload),
1093                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
1094                  I(-TICKSPERSEC));
1095
1096     /*
1097      * The Terminal/Features panel.
1098      */
1099     ctrl_settitle(b, "Terminal/Features",
1100                   "Enabling and disabling advanced terminal features");
1101
1102     s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1103     ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1104                   HELPCTX(features_application),
1105                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));
1106     ctrl_checkbox(s, "Disable application keypad mode", 'k',
1107                   HELPCTX(features_application),
1108                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));
1109     ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1110                   HELPCTX(features_mouse),
1111                   dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));
1112     ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1113                   HELPCTX(features_resize),
1114                   dlg_stdcheckbox_handler,
1115                   I(offsetof(Config,no_remote_resize)));
1116     ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1117                   HELPCTX(features_altscreen),
1118                   dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));
1119     ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1120                   HELPCTX(features_retitle),
1121                   dlg_stdcheckbox_handler,
1122                   I(offsetof(Config,no_remote_wintitle)));
1123     ctrl_checkbox(s, "Disable remote window title querying (SECURITY)",
1124                   'q', HELPCTX(features_qtitle), dlg_stdcheckbox_handler,
1125                   I(offsetof(Config,no_remote_qtitle)));
1126     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1127                   HELPCTX(features_dbackspace),
1128                   dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
1129     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1130                   'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
1131                   I(offsetof(Config,no_remote_charset)));
1132     ctrl_checkbox(s, "Disable Arabic text shaping",
1133                   'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,
1134                   I(offsetof(Config, arabicshaping)));
1135     ctrl_checkbox(s, "Disable bidirectional text display",
1136                   'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,
1137                   I(offsetof(Config, bidi)));
1138
1139     /*
1140      * The Window panel.
1141      */
1142     str = dupprintf("Options controlling %s's window", appname);
1143     ctrl_settitle(b, "Window", str);
1144     sfree(str);
1145
1146     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1147     ctrl_columns(s, 2, 50, 50);
1148     c = ctrl_editbox(s, "Rows", 'r', 100,
1149                      HELPCTX(window_size),
1150                      dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
1151     c->generic.column = 0;
1152     c = ctrl_editbox(s, "Columns", 'm', 100,
1153                      HELPCTX(window_size),
1154                      dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
1155     c->generic.column = 1;
1156     ctrl_columns(s, 1, 100);
1157
1158     s = ctrl_getset(b, "Window", "scrollback",
1159                     "Control the scrollback in the window");
1160     ctrl_editbox(s, "Lines of scrollback", 's', 50,
1161                  HELPCTX(window_scrollback),
1162                  dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
1163     ctrl_checkbox(s, "Display scrollbar", 'd',
1164                   HELPCTX(window_scrollback),
1165                   dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
1166     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1167                   HELPCTX(window_scrollback),
1168                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
1169     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1170                   HELPCTX(window_scrollback),
1171                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
1172     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1173                   HELPCTX(window_erased),
1174                   dlg_stdcheckbox_handler,
1175                   I(offsetof(Config,erase_to_scrollback)));
1176
1177     /*
1178      * The Window/Appearance panel.
1179      */
1180     str = dupprintf("Configure the appearance of %s's window", appname);
1181     ctrl_settitle(b, "Window/Appearance", str);
1182     sfree(str);
1183
1184     s = ctrl_getset(b, "Window/Appearance", "cursor",
1185                     "Adjust the use of the cursor");
1186     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1187                       HELPCTX(appearance_cursor),
1188                       dlg_stdradiobutton_handler,
1189                       I(offsetof(Config, cursor_type)),
1190                       "Block", 'l', I(0),
1191                       "Underline", 'u', I(1),
1192                       "Vertical line", 'v', I(2), NULL);
1193     ctrl_checkbox(s, "Cursor blinks", 'b',
1194                   HELPCTX(appearance_cursor),
1195                   dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
1196
1197     s = ctrl_getset(b, "Window/Appearance", "font",
1198                     "Font settings");
1199     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1200                  HELPCTX(appearance_font),
1201                  dlg_stdfontsel_handler, I(offsetof(Config, font)));
1202
1203     s = ctrl_getset(b, "Window/Appearance", "mouse",
1204                     "Adjust the use of the mouse pointer");
1205     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1206                   HELPCTX(appearance_hidemouse),
1207                   dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
1208
1209     s = ctrl_getset(b, "Window/Appearance", "border",
1210                     "Adjust the window border");
1211     ctrl_editbox(s, "Gap between text and window edge:", NO_SHORTCUT, 20,
1212                  HELPCTX(appearance_border),
1213                  dlg_stdeditbox_handler,
1214                  I(offsetof(Config,window_border)), I(-1));
1215
1216     /*
1217      * The Window/Behaviour panel.
1218      */
1219     str = dupprintf("Configure the behaviour of %s's window", appname);
1220     ctrl_settitle(b, "Window/Behaviour", str);
1221     sfree(str);
1222
1223     s = ctrl_getset(b, "Window/Behaviour", "title",
1224                     "Adjust the behaviour of the window title");
1225     ctrl_editbox(s, "Window title:", 't', 100,
1226                  HELPCTX(appearance_title),
1227                  dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
1228                  I(sizeof(((Config *)0)->wintitle)));
1229     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1230                   HELPCTX(appearance_title),
1231                   dlg_stdcheckbox_handler,
1232                   I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
1233
1234     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1235     ctrl_checkbox(s, "Warn before closing window", 'w',
1236                   HELPCTX(behaviour_closewarn),
1237                   dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
1238
1239     /*
1240      * The Window/Translation panel.
1241      */
1242     ctrl_settitle(b, "Window/Translation",
1243                   "Options controlling character set translation");
1244
1245     s = ctrl_getset(b, "Window/Translation", "trans",
1246                     "Character set translation on received data");
1247     ctrl_combobox(s, "Received data assumed to be in which character set:",
1248                   'r', 100, HELPCTX(translation_codepage),
1249                   codepage_handler, P(NULL), P(NULL));
1250
1251     str = dupprintf("Adjust how %s handles line drawing characters", appname);
1252     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1253     sfree(str);
1254     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1255                       HELPCTX(translation_linedraw),
1256                       dlg_stdradiobutton_handler,
1257                       I(offsetof(Config, vtmode)),
1258                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1259                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1260                       NULL);
1261     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1262                   HELPCTX(selection_linedraw),
1263                   dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
1264
1265     /*
1266      * The Window/Selection panel.
1267      */
1268     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1269         
1270     s = ctrl_getset(b, "Window/Selection", "mouse",
1271                     "Control use of mouse");
1272     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1273                   HELPCTX(selection_shiftdrag),
1274                   dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
1275     ctrl_radiobuttons(s,
1276                       "Default selection mode (Alt+drag does the other one):",
1277                       NO_SHORTCUT, 2,
1278                       HELPCTX(selection_rect),
1279                       dlg_stdradiobutton_handler,
1280                       I(offsetof(Config, rect_select)),
1281                       "Normal", 'n', I(0),
1282                       "Rectangular block", 'r', I(1), NULL);
1283
1284     s = ctrl_getset(b, "Window/Selection", "charclass",
1285                     "Control the select-one-word-at-a-time mode");
1286     ccd = (struct charclass_data *)
1287         ctrl_alloc(b, sizeof(struct charclass_data));
1288     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1289                                 HELPCTX(selection_charclasses),
1290                                 charclass_handler, P(ccd));
1291     ccd->listbox->listbox.multisel = 1;
1292     ccd->listbox->listbox.ncols = 4;
1293     ccd->listbox->listbox.percentages = snewn(4, int);
1294     ccd->listbox->listbox.percentages[0] = 15;
1295     ccd->listbox->listbox.percentages[1] = 25;
1296     ccd->listbox->listbox.percentages[2] = 20;
1297     ccd->listbox->listbox.percentages[3] = 40;
1298     ctrl_columns(s, 2, 67, 33);
1299     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1300                                 HELPCTX(selection_charclasses),
1301                                 charclass_handler, P(ccd), P(NULL));
1302     ccd->editbox->generic.column = 0;
1303     ccd->button = ctrl_pushbutton(s, "Set", 's',
1304                                   HELPCTX(selection_charclasses),
1305                                   charclass_handler, P(ccd));
1306     ccd->button->generic.column = 1;
1307     ctrl_columns(s, 1, 100);
1308
1309     /*
1310      * The Window/Colours panel.
1311      */
1312     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1313
1314     s = ctrl_getset(b, "Window/Colours", "general",
1315                     "General options for colour usage");
1316     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1317                   HELPCTX(colours_ansi),
1318                   dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));
1319     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1320                   HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,
1321                   I(offsetof(Config,xterm_256_colour)));
1322     ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1323                   HELPCTX(colours_bold),
1324                   dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
1325
1326     str = dupprintf("Adjust the precise colours %s displays", appname);
1327     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1328     sfree(str);
1329     ctrl_text(s, "Select a colour from the list, and then click the"
1330               " Modify button to change its appearance.",
1331               HELPCTX(colours_config));
1332     ctrl_columns(s, 2, 67, 33);
1333     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1334     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1335                                HELPCTX(colours_config), colour_handler, P(cd));
1336     cd->listbox->generic.column = 0;
1337     cd->listbox->listbox.height = 7;
1338     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1339     c->generic.column = 1;
1340     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1341                              colour_handler, P(cd), P(NULL));
1342     cd->redit->generic.column = 1;
1343     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1344                              colour_handler, P(cd), P(NULL));
1345     cd->gedit->generic.column = 1;
1346     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1347                              colour_handler, P(cd), P(NULL));
1348     cd->bedit->generic.column = 1;
1349     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1350                                  colour_handler, P(cd));
1351     cd->button->generic.column = 1;
1352     ctrl_columns(s, 1, 100);
1353
1354     /*
1355      * The Connection panel. This doesn't show up if we're in a
1356      * non-network utility such as pterm. We tell this by being
1357      * passed a protocol < 0.
1358      */
1359     if (protocol >= 0) {
1360         ctrl_settitle(b, "Connection", "Options controlling the connection");
1361
1362         s = ctrl_getset(b, "Connection", "keepalive",
1363                         "Sending of null packets to keep session active");
1364         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1365                      HELPCTX(connection_keepalive),
1366                      dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
1367                      I(-1));
1368
1369         if (!midsession) {
1370             s = ctrl_getset(b, "Connection", "tcp",
1371                             "Low-level TCP connection options");
1372             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1373                           'n', HELPCTX(connection_nodelay),
1374                           dlg_stdcheckbox_handler,
1375                           I(offsetof(Config,tcp_nodelay)));
1376             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1377                           'p', HELPCTX(connection_tcpkeepalive),
1378                           dlg_stdcheckbox_handler,
1379                           I(offsetof(Config,tcp_keepalives)));
1380             s = ctrl_getset(b, "Connection", "ipversion",
1381                           "Internet protocol version");
1382             ctrl_radiobuttons(s, NULL, NO_SHORTCUT,
1383 #ifndef NO_IPV6
1384                           3,
1385 #else
1386                           2,
1387 #endif
1388                           HELPCTX(connection_ipversion),
1389                           dlg_stdradiobutton_handler,
1390                           I(offsetof(Config, addressfamily)),
1391                           "Auto", NO_SHORTCUT, I(ADDRTYPE_UNSPEC),
1392                           "IPv4", NO_SHORTCUT, I(ADDRTYPE_IPV4),
1393 #ifndef NO_IPV6
1394                           "IPv6", NO_SHORTCUT, I(ADDRTYPE_IPV6),
1395 #endif
1396                           NULL);
1397         }
1398
1399         /*
1400          * A sub-panel Connection/Data, containing options that
1401          * decide on data to send to the server.
1402          */
1403         if (!midsession) {
1404             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1405
1406             s = ctrl_getset(b, "Connection/Data", "login",
1407                             "Login details");
1408             ctrl_editbox(s, "Auto-login username", 'u', 50,
1409                          HELPCTX(connection_username),
1410                          dlg_stdeditbox_handler, I(offsetof(Config,username)),
1411                          I(sizeof(((Config *)0)->username)));
1412
1413             s = ctrl_getset(b, "Connection/Data", "term",
1414                             "Terminal details");
1415             ctrl_editbox(s, "Terminal-type string", 't', 50,
1416                          HELPCTX(connection_termtype),
1417                          dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
1418                          I(sizeof(((Config *)0)->termtype)));
1419             ctrl_editbox(s, "Terminal speeds", 's', 50,
1420                          HELPCTX(connection_termspeed),
1421                          dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1422                          I(sizeof(((Config *)0)->termspeed)));
1423
1424             s = ctrl_getset(b, "Connection/Data", "env",
1425                             "Environment variables");
1426             ctrl_columns(s, 2, 80, 20);
1427             ed = (struct environ_data *)
1428                 ctrl_alloc(b, sizeof(struct environ_data));
1429             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1430                                       HELPCTX(telnet_environ),
1431                                       environ_handler, P(ed), P(NULL));
1432             ed->varbox->generic.column = 0;
1433             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1434                                       HELPCTX(telnet_environ),
1435                                       environ_handler, P(ed), P(NULL));
1436             ed->valbox->generic.column = 0;
1437             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1438                                             HELPCTX(telnet_environ),
1439                                             environ_handler, P(ed));
1440             ed->addbutton->generic.column = 1;
1441             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1442                                             HELPCTX(telnet_environ),
1443                                             environ_handler, P(ed));
1444             ed->rembutton->generic.column = 1;
1445             ctrl_columns(s, 1, 100);
1446             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1447                                        HELPCTX(telnet_environ),
1448                                        environ_handler, P(ed));
1449             ed->listbox->listbox.height = 3;
1450             ed->listbox->listbox.ncols = 2;
1451             ed->listbox->listbox.percentages = snewn(2, int);
1452             ed->listbox->listbox.percentages[0] = 30;
1453             ed->listbox->listbox.percentages[1] = 70;
1454         }
1455
1456     }
1457
1458     if (!midsession) {
1459         /*
1460          * The Connection/Proxy panel.
1461          */
1462         ctrl_settitle(b, "Connection/Proxy",
1463                       "Options controlling proxy usage");
1464
1465         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1466         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1467                           HELPCTX(proxy_type),
1468                           dlg_stdradiobutton_handler,
1469                           I(offsetof(Config, proxy_type)),
1470                           "None", I(PROXY_NONE),
1471                           "SOCKS 4", I(PROXY_SOCKS4),
1472                           "SOCKS 5", I(PROXY_SOCKS5),
1473                           "HTTP", I(PROXY_HTTP),
1474                           "Telnet", I(PROXY_TELNET),
1475                           NULL);
1476         ctrl_columns(s, 2, 80, 20);
1477         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1478                          HELPCTX(proxy_main),
1479                          dlg_stdeditbox_handler,
1480                          I(offsetof(Config,proxy_host)),
1481                          I(sizeof(((Config *)0)->proxy_host)));
1482         c->generic.column = 0;
1483         c = ctrl_editbox(s, "Port", 'p', 100,
1484                          HELPCTX(proxy_main),
1485                          dlg_stdeditbox_handler,
1486                          I(offsetof(Config,proxy_port)),
1487                          I(-1));
1488         c->generic.column = 1;
1489         ctrl_columns(s, 1, 100);
1490         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1491                      HELPCTX(proxy_exclude),
1492                      dlg_stdeditbox_handler,
1493                      I(offsetof(Config,proxy_exclude_list)),
1494                      I(sizeof(((Config *)0)->proxy_exclude_list)));
1495         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1496                       HELPCTX(proxy_exclude),
1497                       dlg_stdcheckbox_handler,
1498                       I(offsetof(Config,even_proxy_localhost)));
1499         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1500                           HELPCTX(proxy_dns),
1501                           dlg_stdradiobutton_handler,
1502                           I(offsetof(Config, proxy_dns)),
1503                           "No", I(FORCE_OFF),
1504                           "Auto", I(AUTO),
1505                           "Yes", I(FORCE_ON), NULL);
1506         ctrl_editbox(s, "Username", 'u', 60,
1507                      HELPCTX(proxy_auth),
1508                      dlg_stdeditbox_handler,
1509                      I(offsetof(Config,proxy_username)),
1510                      I(sizeof(((Config *)0)->proxy_username)));
1511         c = ctrl_editbox(s, "Password", 'w', 60,
1512                          HELPCTX(proxy_auth),
1513                          dlg_stdeditbox_handler,
1514                          I(offsetof(Config,proxy_password)),
1515                          I(sizeof(((Config *)0)->proxy_password)));
1516         c->editbox.password = 1;
1517         ctrl_editbox(s, "Telnet command", 'm', 100,
1518                      HELPCTX(proxy_command),
1519                      dlg_stdeditbox_handler,
1520                      I(offsetof(Config,proxy_telnet_command)),
1521                      I(sizeof(((Config *)0)->proxy_telnet_command)));
1522     }
1523
1524     /*
1525      * The Telnet panel exists in the base config box, and in a
1526      * mid-session reconfig box _if_ we're using Telnet.
1527      */
1528     if (!midsession || protocol == PROT_TELNET) {
1529         /*
1530          * The Connection/Telnet panel.
1531          */
1532         ctrl_settitle(b, "Connection/Telnet",
1533                       "Options controlling Telnet connections");
1534
1535         s = ctrl_getset(b, "Connection/Telnet", "protocol",
1536                         "Telnet protocol adjustments");
1537
1538         if (!midsession) {
1539             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
1540                               NO_SHORTCUT, 2,
1541                               HELPCTX(telnet_oldenviron),
1542                               dlg_stdradiobutton_handler,
1543                               I(offsetof(Config, rfc_environ)),
1544                               "BSD (commonplace)", 'b', I(0),
1545                               "RFC 1408 (unusual)", 'f', I(1), NULL);
1546             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
1547                               HELPCTX(telnet_passive),
1548                               dlg_stdradiobutton_handler,
1549                               I(offsetof(Config, passive_telnet)),
1550                               "Passive", I(1), "Active", I(0), NULL);
1551         }
1552         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
1553                       HELPCTX(telnet_specialkeys),
1554                       dlg_stdcheckbox_handler,
1555                       I(offsetof(Config,telnet_keyboard)));
1556         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
1557                       'm', HELPCTX(telnet_newline),
1558                       dlg_stdcheckbox_handler,
1559                       I(offsetof(Config,telnet_newline)));
1560     }
1561
1562     if (!midsession) {
1563
1564         /*
1565          * The Connection/Rlogin panel.
1566          */
1567         ctrl_settitle(b, "Connection/Rlogin",
1568                       "Options controlling Rlogin connections");
1569
1570         s = ctrl_getset(b, "Connection/Rlogin", "data",
1571                         "Data to send to the server");
1572         ctrl_editbox(s, "Local username:", 'l', 50,
1573                      HELPCTX(rlogin_localuser),
1574                      dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
1575                      I(sizeof(((Config *)0)->localusername)));
1576
1577     }
1578
1579     /*
1580      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
1581      * when we're not doing SSH.
1582      */
1583
1584     if (backends[3].name != NULL && (!midsession || protocol == PROT_SSH)) {
1585
1586         /*
1587          * The Connection/SSH panel.
1588          */
1589         ctrl_settitle(b, "Connection/SSH",
1590                       "Options controlling SSH connections");
1591
1592         if (midsession && protcfginfo == 1) {
1593             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
1594             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
1595                       "session; it is only here so that sub-panels of it can "
1596                       "exist without looking strange.", HELPCTX(no_help));
1597         }
1598
1599         if (!midsession) {
1600
1601             s = ctrl_getset(b, "Connection/SSH", "data",
1602                             "Data to send to the server");
1603             ctrl_editbox(s, "Remote command:", 'r', 100,
1604                          HELPCTX(ssh_command),
1605                          dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
1606                          I(sizeof(((Config *)0)->remote_cmd)));
1607
1608             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1609             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
1610                           HELPCTX(ssh_nopty),
1611                           dlg_stdcheckbox_handler,
1612                           I(offsetof(Config,nopty)));
1613             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
1614                           HELPCTX(ssh_noshell),
1615                           dlg_stdcheckbox_handler,
1616                           I(offsetof(Config,ssh_no_shell)));
1617         }
1618
1619         if (!midsession || protcfginfo != 1) {
1620             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1621
1622             ctrl_checkbox(s, "Enable compression", 'e',
1623                           HELPCTX(ssh_compress),
1624                           dlg_stdcheckbox_handler,
1625                           I(offsetof(Config,compression)));
1626         }
1627
1628         if (!midsession) {
1629             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1630
1631             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
1632                               HELPCTX(ssh_protocol),
1633                               dlg_stdradiobutton_handler,
1634                               I(offsetof(Config, sshprot)),
1635                               "1 only", 'l', I(0),
1636                               "1", '1', I(1),
1637                               "2", '2', I(2),
1638                               "2 only", 'y', I(3), NULL);
1639         }
1640
1641         if (!midsession || protcfginfo != 1) {
1642             s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
1643             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
1644                               HELPCTX(ssh_ciphers),
1645                               cipherlist_handler, P(NULL));
1646             c->listbox.height = 6;
1647
1648             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH 2", 'i',
1649                           HELPCTX(ssh_ciphers),
1650                           dlg_stdcheckbox_handler,
1651                           I(offsetof(Config,ssh2_des_cbc)));
1652         }
1653
1654         /*
1655          * The Connection/SSH/Kex panel. (Owing to repeat key
1656          * exchange, this is all meaningful in mid-session _if_
1657          * we're using SSH2 or haven't decided yet.)
1658          */
1659         if (protcfginfo != 1) {
1660             ctrl_settitle(b, "Connection/SSH/Kex",
1661                           "Options controlling SSH key exchange");
1662
1663             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
1664                             "Key exchange algorithm options");
1665             c = ctrl_draglist(s, "Algorithm selection policy", 's',
1666                               HELPCTX(ssh_kexlist),
1667                               kexlist_handler, P(NULL));
1668             c->listbox.height = 5;
1669
1670             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
1671                             "Options controlling key re-exchange");
1672
1673             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
1674                          HELPCTX(ssh_kex_repeat),
1675                          dlg_stdeditbox_handler,
1676                          I(offsetof(Config,ssh_rekey_time)),
1677                          I(-1));
1678             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
1679                          HELPCTX(ssh_kex_repeat),
1680                          dlg_stdeditbox_handler,
1681                          I(offsetof(Config,ssh_rekey_data)),
1682                          I(16));
1683             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
1684                       HELPCTX(ssh_kex_repeat));
1685         }
1686
1687         if (!midsession) {
1688
1689             /*
1690              * The Connection/SSH/Auth panel.
1691              */
1692             ctrl_settitle(b, "Connection/SSH/Auth",
1693                           "Options controlling SSH authentication");
1694
1695             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
1696                             "Authentication methods");
1697             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH1)", 'm',
1698                           HELPCTX(ssh_auth_tis),
1699                           dlg_stdcheckbox_handler,
1700                           I(offsetof(Config,try_tis_auth)));
1701             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH2)",
1702                           'i', HELPCTX(ssh_auth_ki),
1703                           dlg_stdcheckbox_handler,
1704                           I(offsetof(Config,try_ki_auth)));
1705
1706             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
1707                             "Authentication parameters");
1708             ctrl_checkbox(s, "Allow agent forwarding", 'f',
1709                           HELPCTX(ssh_auth_agentfwd),
1710                           dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
1711             ctrl_checkbox(s, "Allow attempted changes of username in SSH2", 'u',
1712                           HELPCTX(ssh_auth_changeuser),
1713                           dlg_stdcheckbox_handler,
1714                           I(offsetof(Config,change_username)));
1715             ctrl_filesel(s, "Private key file for authentication:", 'k',
1716                          FILTER_KEY_FILES, FALSE, "Select private key file",
1717                          HELPCTX(ssh_auth_privkey),
1718                          dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
1719         }
1720
1721         if (!midsession) {
1722             /*
1723              * The Connection/SSH/X11 panel.
1724              */
1725             ctrl_settitle(b, "Connection/SSH/X11",
1726                           "Options controlling SSH X11 forwarding");
1727
1728             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
1729             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
1730                           HELPCTX(ssh_tunnels_x11),
1731                           dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
1732             ctrl_editbox(s, "X display location", 'x', 50,
1733                          HELPCTX(ssh_tunnels_x11),
1734                          dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
1735                          I(sizeof(((Config *)0)->x11_display)));
1736             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
1737                               HELPCTX(ssh_tunnels_x11auth),
1738                               dlg_stdradiobutton_handler,
1739                               I(offsetof(Config, x11_auth)),
1740                               "MIT-Magic-Cookie-1", I(X11_MIT),
1741                               "XDM-Authorization-1", I(X11_XDM), NULL);
1742         }
1743
1744         /*
1745          * The Tunnels panel _is_ still available in mid-session.
1746          */
1747         ctrl_settitle(b, "Connection/SSH/Tunnels",
1748                       "Options controlling SSH port forwarding");
1749
1750         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
1751                         "Port forwarding");
1752         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
1753                       HELPCTX(ssh_tunnels_portfwd_localhost),
1754                       dlg_stdcheckbox_handler,
1755                       I(offsetof(Config,lport_acceptall)));
1756         ctrl_checkbox(s, "Remote ports do the same (SSH v2 only)", 'p',
1757                       HELPCTX(ssh_tunnels_portfwd_localhost),
1758                       dlg_stdcheckbox_handler,
1759                       I(offsetof(Config,rport_acceptall)));
1760
1761         ctrl_columns(s, 3, 55, 20, 25);
1762         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
1763         c->generic.column = COLUMN_FIELD(0,2);
1764         /* You want to select from the list, _then_ hit Remove. So tab order
1765          * should be that way round. */
1766         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
1767         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1768                                          HELPCTX(ssh_tunnels_portfwd),
1769                                          portfwd_handler, P(pfd));
1770         pfd->rembutton->generic.column = 2;
1771         pfd->rembutton->generic.tabdelay = 1;
1772         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1773                                     HELPCTX(ssh_tunnels_portfwd),
1774                                     portfwd_handler, P(pfd));
1775         pfd->listbox->listbox.height = 3;
1776         pfd->listbox->listbox.ncols = 2;
1777         pfd->listbox->listbox.percentages = snewn(2, int);
1778         pfd->listbox->listbox.percentages[0] = 20;
1779         pfd->listbox->listbox.percentages[1] = 80;
1780         ctrl_tabdelay(s, pfd->rembutton);
1781         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
1782         /* You want to enter source, destination and type, _then_ hit Add.
1783          * Again, we adjust the tab order to reflect this. */
1784         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
1785                                          HELPCTX(ssh_tunnels_portfwd),
1786                                          portfwd_handler, P(pfd));
1787         pfd->addbutton->generic.column = 2;
1788         pfd->addbutton->generic.tabdelay = 1;
1789         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
1790                                       HELPCTX(ssh_tunnels_portfwd),
1791                                       portfwd_handler, P(pfd), P(NULL));
1792         pfd->sourcebox->generic.column = 0;
1793         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
1794                                     HELPCTX(ssh_tunnels_portfwd),
1795                                     portfwd_handler, P(pfd), P(NULL));
1796         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1797                                            HELPCTX(ssh_tunnels_portfwd),
1798                                            portfwd_handler, P(pfd),
1799                                            "Local", 'l', P(NULL),
1800                                            "Remote", 'm', P(NULL),
1801                                            "Dynamic", 'y', P(NULL),
1802                                            NULL);
1803         pfd->addressfamily =
1804             ctrl_radiobuttons(s, NULL, NO_SHORTCUT,
1805 #ifndef NO_IPV6
1806                               3,
1807 #else
1808                               2,
1809 #endif
1810                               HELPCTX(ssh_tunnels_portfwd_ipversion),
1811                               portfwd_handler, P(pfd),
1812                               "Auto", NO_SHORTCUT, I(ADDRTYPE_UNSPEC),
1813                               "IPv4", NO_SHORTCUT, I(ADDRTYPE_IPV4),
1814 #ifndef NO_IPV6
1815                               "IPv6", NO_SHORTCUT, I(ADDRTYPE_IPV6),
1816 #endif
1817                               NULL);
1818         ctrl_tabdelay(s, pfd->addbutton);
1819         ctrl_columns(s, 1, 100);
1820
1821         if (!midsession) {
1822             /*
1823              * The Connection/SSH/Bugs panel.
1824              */
1825             ctrl_settitle(b, "Connection/SSH/Bugs",
1826                           "Workarounds for SSH server bugs");
1827
1828             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
1829                             "Detection of known bugs in SSH servers");
1830             ctrl_droplist(s, "Chokes on SSH1 ignore messages", 'i', 20,
1831                           HELPCTX(ssh_bugs_ignore1),
1832                           sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
1833             ctrl_droplist(s, "Refuses all SSH1 password camouflage", 's', 20,
1834                           HELPCTX(ssh_bugs_plainpw1),
1835                           sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
1836             ctrl_droplist(s, "Chokes on SSH1 RSA authentication", 'r', 20,
1837                           HELPCTX(ssh_bugs_rsa1),
1838                           sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
1839             ctrl_droplist(s, "Miscomputes SSH2 HMAC keys", 'm', 20,
1840                           HELPCTX(ssh_bugs_hmac2),
1841                           sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
1842             ctrl_droplist(s, "Miscomputes SSH2 encryption keys", 'e', 20,
1843                           HELPCTX(ssh_bugs_derivekey2),
1844                           sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
1845             ctrl_droplist(s, "Requires padding on SSH2 RSA signatures", 'p', 20,
1846                           HELPCTX(ssh_bugs_rsapad2),
1847                           sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
1848             ctrl_droplist(s, "Misuses the session ID in PK auth", 'n', 20,
1849                           HELPCTX(ssh_bugs_pksessid2),
1850                           sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
1851         }
1852     }
1853 }