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