]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Patch inspired by one from Daniel Silverstone in Debian bug #229232:
[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, *sshrawlogname;
1175         if ((midsession && protocol == PROT_SSH) ||
1176             (!midsession && backends[3].name != NULL)) {
1177             sshlogname = "SSH packets";
1178             sshrawlogname = "SSH packets and raw data";
1179         } else {
1180             sshlogname = NULL;         /* this will disable both buttons */
1181             sshrawlogname = NULL;      /* this will just placate optimisers */
1182         }
1183         ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1184                           HELPCTX(logging_main),
1185                           loggingbuttons_handler,
1186                           I(offsetof(Config, logtype)),
1187                           "None", 't', I(LGTYP_NONE),
1188                           "Printable output", 'p', I(LGTYP_ASCII),
1189                           "All session output", 'l', I(LGTYP_DEBUG),
1190                           sshlogname, 's', I(LGTYP_PACKETS),
1191                           sshrawlogname, 'r', I(LGTYP_SSHRAW),
1192                           NULL);
1193     }
1194     ctrl_filesel(s, "Log file name:", 'f',
1195                  NULL, TRUE, "Select session log file name",
1196                  HELPCTX(logging_filename),
1197                  dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));
1198     ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1199               " &T for time, and &H for host name)",
1200               HELPCTX(logging_filename));
1201     ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1202                       HELPCTX(logging_exists),
1203                       dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),
1204                       "Always overwrite it", I(LGXF_OVR),
1205                       "Always append to the end of it", I(LGXF_APN),
1206                       "Ask the user every time", I(LGXF_ASK), NULL);
1207     ctrl_checkbox(s, "Flush log file frequently", 'u',
1208                  HELPCTX(logging_flush),
1209                  dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));
1210
1211     if ((midsession && protocol == PROT_SSH) ||
1212         (!midsession && backends[3].name != NULL)) {
1213         s = ctrl_getset(b, "Session/Logging", "ssh",
1214                         "Options specific to SSH packet logging");
1215         ctrl_checkbox(s, "Omit known password fields", 'k',
1216                       HELPCTX(logging_ssh_omit_password),
1217                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass)));
1218         ctrl_checkbox(s, "Omit session data", 'd',
1219                       HELPCTX(logging_ssh_omit_data),
1220                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata)));
1221     }
1222
1223     /*
1224      * The Terminal panel.
1225      */
1226     ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1227
1228     s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1229     ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1230                   HELPCTX(terminal_autowrap),
1231                   dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));
1232     ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1233                   HELPCTX(terminal_decom),
1234                   dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));
1235     ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1236                   HELPCTX(terminal_lfhascr),
1237                   dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
1238     ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1239                   HELPCTX(terminal_bce),
1240                   dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
1241     ctrl_checkbox(s, "Enable blinking text", 'n',
1242                   HELPCTX(terminal_blink),
1243                   dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));
1244     ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1245                  HELPCTX(terminal_answerback),
1246                  dlg_stdeditbox_handler, I(offsetof(Config,answerback)),
1247                  I(sizeof(((Config *)0)->answerback)));
1248
1249     s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1250     ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1251                       HELPCTX(terminal_localecho),
1252                       dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),
1253                       "Auto", I(AUTO),
1254                       "Force on", I(FORCE_ON),
1255                       "Force off", I(FORCE_OFF), NULL);
1256     ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1257                       HELPCTX(terminal_localedit),
1258                       dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),
1259                       "Auto", I(AUTO),
1260                       "Force on", I(FORCE_ON),
1261                       "Force off", I(FORCE_OFF), NULL);
1262
1263     s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1264     ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1265                   HELPCTX(terminal_printing),
1266                   printerbox_handler, P(NULL), P(NULL));
1267
1268     /*
1269      * The Terminal/Keyboard panel.
1270      */
1271     ctrl_settitle(b, "Terminal/Keyboard",
1272                   "Options controlling the effects of keys");
1273
1274     s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1275                     "Change the sequences sent by:");
1276     ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1277                       HELPCTX(keyboard_backspace),
1278                       dlg_stdradiobutton_handler,
1279                       I(offsetof(Config, bksp_is_delete)),
1280                       "Control-H", I(0), "Control-? (127)", I(1), NULL);
1281     ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1282                       HELPCTX(keyboard_homeend),
1283                       dlg_stdradiobutton_handler,
1284                       I(offsetof(Config, rxvt_homeend)),
1285                       "Standard", I(0), "rxvt", I(1), NULL);
1286     ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1287                       HELPCTX(keyboard_funkeys),
1288                       dlg_stdradiobutton_handler,
1289                       I(offsetof(Config, funky_type)),
1290                       "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1291                       "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1292
1293     s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1294                     "Application keypad settings:");
1295     ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1296                       HELPCTX(keyboard_appcursor),
1297                       dlg_stdradiobutton_handler,
1298                       I(offsetof(Config, app_cursor)),
1299                       "Normal", I(0), "Application", I(1), NULL);
1300     ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1301                       HELPCTX(keyboard_appkeypad),
1302                       numeric_keypad_handler, P(NULL),
1303                       "Normal", I(0), "Application", I(1), "NetHack", I(2),
1304                       NULL);
1305
1306     /*
1307      * The Terminal/Bell panel.
1308      */
1309     ctrl_settitle(b, "Terminal/Bell",
1310                   "Options controlling the terminal bell");
1311
1312     s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1313     ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1314                       HELPCTX(bell_style),
1315                       dlg_stdradiobutton_handler, I(offsetof(Config, beep)),
1316                       "None (bell disabled)", I(BELL_DISABLED),
1317                       "Make default system alert sound", I(BELL_DEFAULT),
1318                       "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1319
1320     s = ctrl_getset(b, "Terminal/Bell", "overload",
1321                     "Control the bell overload behaviour");
1322     ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1323                   HELPCTX(bell_overload),
1324                   dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));
1325     ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1326                  HELPCTX(bell_overload),
1327                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));
1328     ctrl_editbox(s, "... in this many seconds", 't', 20,
1329                  HELPCTX(bell_overload),
1330                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
1331                  I(-TICKSPERSEC));
1332     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1333               HELPCTX(bell_overload));
1334     ctrl_editbox(s, "Seconds of silence required", 's', 20,
1335                  HELPCTX(bell_overload),
1336                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
1337                  I(-TICKSPERSEC));
1338
1339     /*
1340      * The Terminal/Features panel.
1341      */
1342     ctrl_settitle(b, "Terminal/Features",
1343                   "Enabling and disabling advanced terminal features");
1344
1345     s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1346     ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1347                   HELPCTX(features_application),
1348                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));
1349     ctrl_checkbox(s, "Disable application keypad mode", 'k',
1350                   HELPCTX(features_application),
1351                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));
1352     ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1353                   HELPCTX(features_mouse),
1354                   dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));
1355     ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1356                   HELPCTX(features_resize),
1357                   dlg_stdcheckbox_handler,
1358                   I(offsetof(Config,no_remote_resize)));
1359     ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1360                   HELPCTX(features_altscreen),
1361                   dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));
1362     ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1363                   HELPCTX(features_retitle),
1364                   dlg_stdcheckbox_handler,
1365                   I(offsetof(Config,no_remote_wintitle)));
1366     ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1367                       HELPCTX(features_qtitle),
1368                       dlg_stdradiobutton_handler,
1369                       I(offsetof(Config,remote_qtitle_action)),
1370                       "None", I(TITLE_NONE),
1371                       "Empty string", I(TITLE_EMPTY),
1372                       "Window title", I(TITLE_REAL), NULL);
1373     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1374                   HELPCTX(features_dbackspace),
1375                   dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
1376     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1377                   'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
1378                   I(offsetof(Config,no_remote_charset)));
1379     ctrl_checkbox(s, "Disable Arabic text shaping",
1380                   'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,
1381                   I(offsetof(Config, arabicshaping)));
1382     ctrl_checkbox(s, "Disable bidirectional text display",
1383                   'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,
1384                   I(offsetof(Config, bidi)));
1385
1386     /*
1387      * The Window panel.
1388      */
1389     str = dupprintf("Options controlling %s's window", appname);
1390     ctrl_settitle(b, "Window", str);
1391     sfree(str);
1392
1393     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1394     ctrl_columns(s, 2, 50, 50);
1395     c = ctrl_editbox(s, "Rows", 'r', 100,
1396                      HELPCTX(window_size),
1397                      dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
1398     c->generic.column = 0;
1399     c = ctrl_editbox(s, "Columns", 'm', 100,
1400                      HELPCTX(window_size),
1401                      dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
1402     c->generic.column = 1;
1403     ctrl_columns(s, 1, 100);
1404
1405     s = ctrl_getset(b, "Window", "scrollback",
1406                     "Control the scrollback in the window");
1407     ctrl_editbox(s, "Lines of scrollback", 's', 50,
1408                  HELPCTX(window_scrollback),
1409                  dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
1410     ctrl_checkbox(s, "Display scrollbar", 'd',
1411                   HELPCTX(window_scrollback),
1412                   dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
1413     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1414                   HELPCTX(window_scrollback),
1415                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
1416     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1417                   HELPCTX(window_scrollback),
1418                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
1419     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1420                   HELPCTX(window_erased),
1421                   dlg_stdcheckbox_handler,
1422                   I(offsetof(Config,erase_to_scrollback)));
1423
1424     /*
1425      * The Window/Appearance panel.
1426      */
1427     str = dupprintf("Configure the appearance of %s's window", appname);
1428     ctrl_settitle(b, "Window/Appearance", str);
1429     sfree(str);
1430
1431     s = ctrl_getset(b, "Window/Appearance", "cursor",
1432                     "Adjust the use of the cursor");
1433     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1434                       HELPCTX(appearance_cursor),
1435                       dlg_stdradiobutton_handler,
1436                       I(offsetof(Config, cursor_type)),
1437                       "Block", 'l', I(0),
1438                       "Underline", 'u', I(1),
1439                       "Vertical line", 'v', I(2), NULL);
1440     ctrl_checkbox(s, "Cursor blinks", 'b',
1441                   HELPCTX(appearance_cursor),
1442                   dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
1443
1444     s = ctrl_getset(b, "Window/Appearance", "font",
1445                     "Font settings");
1446     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1447                  HELPCTX(appearance_font),
1448                  dlg_stdfontsel_handler, I(offsetof(Config, font)));
1449
1450     s = ctrl_getset(b, "Window/Appearance", "mouse",
1451                     "Adjust the use of the mouse pointer");
1452     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1453                   HELPCTX(appearance_hidemouse),
1454                   dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
1455
1456     s = ctrl_getset(b, "Window/Appearance", "border",
1457                     "Adjust the window border");
1458     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1459                  HELPCTX(appearance_border),
1460                  dlg_stdeditbox_handler,
1461                  I(offsetof(Config,window_border)), I(-1));
1462
1463     /*
1464      * The Window/Behaviour panel.
1465      */
1466     str = dupprintf("Configure the behaviour of %s's window", appname);
1467     ctrl_settitle(b, "Window/Behaviour", str);
1468     sfree(str);
1469
1470     s = ctrl_getset(b, "Window/Behaviour", "title",
1471                     "Adjust the behaviour of the window title");
1472     ctrl_editbox(s, "Window title:", 't', 100,
1473                  HELPCTX(appearance_title),
1474                  dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
1475                  I(sizeof(((Config *)0)->wintitle)));
1476     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1477                   HELPCTX(appearance_title),
1478                   dlg_stdcheckbox_handler,
1479                   I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
1480
1481     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1482     ctrl_checkbox(s, "Warn before closing window", 'w',
1483                   HELPCTX(behaviour_closewarn),
1484                   dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
1485
1486     /*
1487      * The Window/Translation panel.
1488      */
1489     ctrl_settitle(b, "Window/Translation",
1490                   "Options controlling character set translation");
1491
1492     s = ctrl_getset(b, "Window/Translation", "trans",
1493                     "Character set translation on received data");
1494     ctrl_combobox(s, "Received data assumed to be in which character set:",
1495                   'r', 100, HELPCTX(translation_codepage),
1496                   codepage_handler, P(NULL), P(NULL));
1497
1498     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1499     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1500                   HELPCTX(translation_cjk_ambig_wide),
1501                   dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide)));
1502
1503     str = dupprintf("Adjust how %s handles line drawing characters", appname);
1504     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1505     sfree(str);
1506     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1507                       HELPCTX(translation_linedraw),
1508                       dlg_stdradiobutton_handler,
1509                       I(offsetof(Config, vtmode)),
1510                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1511                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1512                       NULL);
1513     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1514                   HELPCTX(selection_linedraw),
1515                   dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
1516
1517     /*
1518      * The Window/Selection panel.
1519      */
1520     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1521         
1522     s = ctrl_getset(b, "Window/Selection", "mouse",
1523                     "Control use of mouse");
1524     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1525                   HELPCTX(selection_shiftdrag),
1526                   dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
1527     ctrl_radiobuttons(s,
1528                       "Default selection mode (Alt+drag does the other one):",
1529                       NO_SHORTCUT, 2,
1530                       HELPCTX(selection_rect),
1531                       dlg_stdradiobutton_handler,
1532                       I(offsetof(Config, rect_select)),
1533                       "Normal", 'n', I(0),
1534                       "Rectangular block", 'r', I(1), NULL);
1535
1536     s = ctrl_getset(b, "Window/Selection", "charclass",
1537                     "Control the select-one-word-at-a-time mode");
1538     ccd = (struct charclass_data *)
1539         ctrl_alloc(b, sizeof(struct charclass_data));
1540     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1541                                 HELPCTX(selection_charclasses),
1542                                 charclass_handler, P(ccd));
1543     ccd->listbox->listbox.multisel = 1;
1544     ccd->listbox->listbox.ncols = 4;
1545     ccd->listbox->listbox.percentages = snewn(4, int);
1546     ccd->listbox->listbox.percentages[0] = 15;
1547     ccd->listbox->listbox.percentages[1] = 25;
1548     ccd->listbox->listbox.percentages[2] = 20;
1549     ccd->listbox->listbox.percentages[3] = 40;
1550     ctrl_columns(s, 2, 67, 33);
1551     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1552                                 HELPCTX(selection_charclasses),
1553                                 charclass_handler, P(ccd), P(NULL));
1554     ccd->editbox->generic.column = 0;
1555     ccd->button = ctrl_pushbutton(s, "Set", 's',
1556                                   HELPCTX(selection_charclasses),
1557                                   charclass_handler, P(ccd));
1558     ccd->button->generic.column = 1;
1559     ctrl_columns(s, 1, 100);
1560
1561     /*
1562      * The Window/Colours panel.
1563      */
1564     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1565
1566     s = ctrl_getset(b, "Window/Colours", "general",
1567                     "General options for colour usage");
1568     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1569                   HELPCTX(colours_ansi),
1570                   dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));
1571     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1572                   HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,
1573                   I(offsetof(Config,xterm_256_colour)));
1574     ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1575                   HELPCTX(colours_bold),
1576                   dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
1577
1578     str = dupprintf("Adjust the precise colours %s displays", appname);
1579     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1580     sfree(str);
1581     ctrl_text(s, "Select a colour from the list, and then click the"
1582               " Modify button to change its appearance.",
1583               HELPCTX(colours_config));
1584     ctrl_columns(s, 2, 67, 33);
1585     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1586     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1587                                HELPCTX(colours_config), colour_handler, P(cd));
1588     cd->listbox->generic.column = 0;
1589     cd->listbox->listbox.height = 7;
1590     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1591     c->generic.column = 1;
1592     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1593                              colour_handler, P(cd), P(NULL));
1594     cd->redit->generic.column = 1;
1595     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1596                              colour_handler, P(cd), P(NULL));
1597     cd->gedit->generic.column = 1;
1598     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1599                              colour_handler, P(cd), P(NULL));
1600     cd->bedit->generic.column = 1;
1601     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1602                                  colour_handler, P(cd));
1603     cd->button->generic.column = 1;
1604     ctrl_columns(s, 1, 100);
1605
1606     /*
1607      * The Connection panel. This doesn't show up if we're in a
1608      * non-network utility such as pterm. We tell this by being
1609      * passed a protocol < 0.
1610      */
1611     if (protocol >= 0) {
1612         ctrl_settitle(b, "Connection", "Options controlling the connection");
1613
1614         s = ctrl_getset(b, "Connection", "keepalive",
1615                         "Sending of null packets to keep session active");
1616         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1617                      HELPCTX(connection_keepalive),
1618                      dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
1619                      I(-1));
1620
1621         if (!midsession) {
1622             s = ctrl_getset(b, "Connection", "tcp",
1623                             "Low-level TCP connection options");
1624             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1625                           'n', HELPCTX(connection_nodelay),
1626                           dlg_stdcheckbox_handler,
1627                           I(offsetof(Config,tcp_nodelay)));
1628             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1629                           'p', HELPCTX(connection_tcpkeepalive),
1630                           dlg_stdcheckbox_handler,
1631                           I(offsetof(Config,tcp_keepalives)));
1632 #ifndef NO_IPV6
1633             s = ctrl_getset(b, "Connection", "ipversion",
1634                           "Internet protocol version");
1635             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1636                           HELPCTX(connection_ipversion),
1637                           dlg_stdradiobutton_handler,
1638                           I(offsetof(Config, addressfamily)),
1639                           "Auto", 'u', I(ADDRTYPE_UNSPEC),
1640                           "IPv4", '4', I(ADDRTYPE_IPV4),
1641                           "IPv6", '6', I(ADDRTYPE_IPV6),
1642                           NULL);
1643 #endif
1644         }
1645
1646         /*
1647          * A sub-panel Connection/Data, containing options that
1648          * decide on data to send to the server.
1649          */
1650         if (!midsession) {
1651             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1652
1653             s = ctrl_getset(b, "Connection/Data", "login",
1654                             "Login details");
1655             ctrl_editbox(s, "Auto-login username", 'u', 50,
1656                          HELPCTX(connection_username),
1657                          dlg_stdeditbox_handler, I(offsetof(Config,username)),
1658                          I(sizeof(((Config *)0)->username)));
1659
1660             s = ctrl_getset(b, "Connection/Data", "term",
1661                             "Terminal details");
1662             ctrl_editbox(s, "Terminal-type string", 't', 50,
1663                          HELPCTX(connection_termtype),
1664                          dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
1665                          I(sizeof(((Config *)0)->termtype)));
1666             ctrl_editbox(s, "Terminal speeds", 's', 50,
1667                          HELPCTX(connection_termspeed),
1668                          dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1669                          I(sizeof(((Config *)0)->termspeed)));
1670
1671             s = ctrl_getset(b, "Connection/Data", "env",
1672                             "Environment variables");
1673             ctrl_columns(s, 2, 80, 20);
1674             ed = (struct environ_data *)
1675                 ctrl_alloc(b, sizeof(struct environ_data));
1676             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1677                                       HELPCTX(telnet_environ),
1678                                       environ_handler, P(ed), P(NULL));
1679             ed->varbox->generic.column = 0;
1680             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1681                                       HELPCTX(telnet_environ),
1682                                       environ_handler, P(ed), P(NULL));
1683             ed->valbox->generic.column = 0;
1684             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1685                                             HELPCTX(telnet_environ),
1686                                             environ_handler, P(ed));
1687             ed->addbutton->generic.column = 1;
1688             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1689                                             HELPCTX(telnet_environ),
1690                                             environ_handler, P(ed));
1691             ed->rembutton->generic.column = 1;
1692             ctrl_columns(s, 1, 100);
1693             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1694                                        HELPCTX(telnet_environ),
1695                                        environ_handler, P(ed));
1696             ed->listbox->listbox.height = 3;
1697             ed->listbox->listbox.ncols = 2;
1698             ed->listbox->listbox.percentages = snewn(2, int);
1699             ed->listbox->listbox.percentages[0] = 30;
1700             ed->listbox->listbox.percentages[1] = 70;
1701         }
1702
1703     }
1704
1705     if (!midsession) {
1706         /*
1707          * The Connection/Proxy panel.
1708          */
1709         ctrl_settitle(b, "Connection/Proxy",
1710                       "Options controlling proxy usage");
1711
1712         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1713         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1714                           HELPCTX(proxy_type),
1715                           dlg_stdradiobutton_handler,
1716                           I(offsetof(Config, proxy_type)),
1717                           "None", I(PROXY_NONE),
1718                           "SOCKS 4", I(PROXY_SOCKS4),
1719                           "SOCKS 5", I(PROXY_SOCKS5),
1720                           "HTTP", I(PROXY_HTTP),
1721                           "Telnet", I(PROXY_TELNET),
1722                           NULL);
1723         ctrl_columns(s, 2, 80, 20);
1724         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1725                          HELPCTX(proxy_main),
1726                          dlg_stdeditbox_handler,
1727                          I(offsetof(Config,proxy_host)),
1728                          I(sizeof(((Config *)0)->proxy_host)));
1729         c->generic.column = 0;
1730         c = ctrl_editbox(s, "Port", 'p', 100,
1731                          HELPCTX(proxy_main),
1732                          dlg_stdeditbox_handler,
1733                          I(offsetof(Config,proxy_port)),
1734                          I(-1));
1735         c->generic.column = 1;
1736         ctrl_columns(s, 1, 100);
1737         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1738                      HELPCTX(proxy_exclude),
1739                      dlg_stdeditbox_handler,
1740                      I(offsetof(Config,proxy_exclude_list)),
1741                      I(sizeof(((Config *)0)->proxy_exclude_list)));
1742         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1743                       HELPCTX(proxy_exclude),
1744                       dlg_stdcheckbox_handler,
1745                       I(offsetof(Config,even_proxy_localhost)));
1746         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1747                           HELPCTX(proxy_dns),
1748                           dlg_stdradiobutton_handler,
1749                           I(offsetof(Config, proxy_dns)),
1750                           "No", I(FORCE_OFF),
1751                           "Auto", I(AUTO),
1752                           "Yes", I(FORCE_ON), NULL);
1753         ctrl_editbox(s, "Username", 'u', 60,
1754                      HELPCTX(proxy_auth),
1755                      dlg_stdeditbox_handler,
1756                      I(offsetof(Config,proxy_username)),
1757                      I(sizeof(((Config *)0)->proxy_username)));
1758         c = ctrl_editbox(s, "Password", 'w', 60,
1759                          HELPCTX(proxy_auth),
1760                          dlg_stdeditbox_handler,
1761                          I(offsetof(Config,proxy_password)),
1762                          I(sizeof(((Config *)0)->proxy_password)));
1763         c->editbox.password = 1;
1764         ctrl_editbox(s, "Telnet command", 'm', 100,
1765                      HELPCTX(proxy_command),
1766                      dlg_stdeditbox_handler,
1767                      I(offsetof(Config,proxy_telnet_command)),
1768                      I(sizeof(((Config *)0)->proxy_telnet_command)));
1769     }
1770
1771     /*
1772      * The Telnet panel exists in the base config box, and in a
1773      * mid-session reconfig box _if_ we're using Telnet.
1774      */
1775     if (!midsession || protocol == PROT_TELNET) {
1776         /*
1777          * The Connection/Telnet panel.
1778          */
1779         ctrl_settitle(b, "Connection/Telnet",
1780                       "Options controlling Telnet connections");
1781
1782         s = ctrl_getset(b, "Connection/Telnet", "protocol",
1783                         "Telnet protocol adjustments");
1784
1785         if (!midsession) {
1786             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
1787                               NO_SHORTCUT, 2,
1788                               HELPCTX(telnet_oldenviron),
1789                               dlg_stdradiobutton_handler,
1790                               I(offsetof(Config, rfc_environ)),
1791                               "BSD (commonplace)", 'b', I(0),
1792                               "RFC 1408 (unusual)", 'f', I(1), NULL);
1793             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
1794                               HELPCTX(telnet_passive),
1795                               dlg_stdradiobutton_handler,
1796                               I(offsetof(Config, passive_telnet)),
1797                               "Passive", I(1), "Active", I(0), NULL);
1798         }
1799         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
1800                       HELPCTX(telnet_specialkeys),
1801                       dlg_stdcheckbox_handler,
1802                       I(offsetof(Config,telnet_keyboard)));
1803         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
1804                       'm', HELPCTX(telnet_newline),
1805                       dlg_stdcheckbox_handler,
1806                       I(offsetof(Config,telnet_newline)));
1807     }
1808
1809     if (!midsession) {
1810
1811         /*
1812          * The Connection/Rlogin panel.
1813          */
1814         ctrl_settitle(b, "Connection/Rlogin",
1815                       "Options controlling Rlogin connections");
1816
1817         s = ctrl_getset(b, "Connection/Rlogin", "data",
1818                         "Data to send to the server");
1819         ctrl_editbox(s, "Local username:", 'l', 50,
1820                      HELPCTX(rlogin_localuser),
1821                      dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
1822                      I(sizeof(((Config *)0)->localusername)));
1823
1824     }
1825
1826     /*
1827      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
1828      * when we're not doing SSH.
1829      */
1830
1831     if (backends[3].name != NULL && (!midsession || protocol == PROT_SSH)) {
1832
1833         /*
1834          * The Connection/SSH panel.
1835          */
1836         ctrl_settitle(b, "Connection/SSH",
1837                       "Options controlling SSH connections");
1838
1839         if (midsession && protcfginfo == 1) {
1840             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
1841             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
1842                       "session; it is only here so that sub-panels of it can "
1843                       "exist without looking strange.", HELPCTX(no_help));
1844         }
1845
1846         if (!midsession) {
1847
1848             s = ctrl_getset(b, "Connection/SSH", "data",
1849                             "Data to send to the server");
1850             ctrl_editbox(s, "Remote command:", 'r', 100,
1851                          HELPCTX(ssh_command),
1852                          dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
1853                          I(sizeof(((Config *)0)->remote_cmd)));
1854
1855             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1856             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
1857                           HELPCTX(ssh_noshell),
1858                           dlg_stdcheckbox_handler,
1859                           I(offsetof(Config,ssh_no_shell)));
1860         }
1861
1862         if (!midsession || protcfginfo != 1) {
1863             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1864
1865             ctrl_checkbox(s, "Enable compression", 'e',
1866                           HELPCTX(ssh_compress),
1867                           dlg_stdcheckbox_handler,
1868                           I(offsetof(Config,compression)));
1869         }
1870
1871         if (!midsession) {
1872             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1873
1874             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
1875                               HELPCTX(ssh_protocol),
1876                               dlg_stdradiobutton_handler,
1877                               I(offsetof(Config, sshprot)),
1878                               "1 only", 'l', I(0),
1879                               "1", '1', I(1),
1880                               "2", '2', I(2),
1881                               "2 only", 'y', I(3), NULL);
1882         }
1883
1884         if (!midsession || protcfginfo != 1) {
1885             s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
1886             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
1887                               HELPCTX(ssh_ciphers),
1888                               cipherlist_handler, P(NULL));
1889             c->listbox.height = 6;
1890
1891             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
1892                           HELPCTX(ssh_ciphers),
1893                           dlg_stdcheckbox_handler,
1894                           I(offsetof(Config,ssh2_des_cbc)));
1895         }
1896
1897         /*
1898          * The Connection/SSH/Kex panel. (Owing to repeat key
1899          * exchange, this is all meaningful in mid-session _if_
1900          * we're using SSH-2 or haven't decided yet.)
1901          */
1902         if (protcfginfo != 1) {
1903             ctrl_settitle(b, "Connection/SSH/Kex",
1904                           "Options controlling SSH key exchange");
1905
1906             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
1907                             "Key exchange algorithm options");
1908             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
1909                               HELPCTX(ssh_kexlist),
1910                               kexlist_handler, P(NULL));
1911             c->listbox.height = 5;
1912
1913             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
1914                             "Options controlling key re-exchange");
1915
1916             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
1917                          HELPCTX(ssh_kex_repeat),
1918                          dlg_stdeditbox_handler,
1919                          I(offsetof(Config,ssh_rekey_time)),
1920                          I(-1));
1921             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
1922                          HELPCTX(ssh_kex_repeat),
1923                          dlg_stdeditbox_handler,
1924                          I(offsetof(Config,ssh_rekey_data)),
1925                          I(16));
1926             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
1927                       HELPCTX(ssh_kex_repeat));
1928         }
1929
1930         if (!midsession) {
1931
1932             /*
1933              * The Connection/SSH/Auth panel.
1934              */
1935             ctrl_settitle(b, "Connection/SSH/Auth",
1936                           "Options controlling SSH authentication");
1937
1938             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
1939             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
1940                           HELPCTX(ssh_auth_bypass),
1941                           dlg_stdcheckbox_handler,
1942                           I(offsetof(Config,ssh_no_userauth)));
1943
1944             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
1945                             "Authentication methods");
1946             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
1947                           HELPCTX(ssh_auth_pageant),
1948                           dlg_stdcheckbox_handler,
1949                           I(offsetof(Config,tryagent)));
1950             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
1951                           HELPCTX(ssh_auth_tis),
1952                           dlg_stdcheckbox_handler,
1953                           I(offsetof(Config,try_tis_auth)));
1954             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
1955                           'i', HELPCTX(ssh_auth_ki),
1956                           dlg_stdcheckbox_handler,
1957                           I(offsetof(Config,try_ki_auth)));
1958
1959             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
1960                             "Authentication parameters");
1961             ctrl_checkbox(s, "Allow agent forwarding", 'f',
1962                           HELPCTX(ssh_auth_agentfwd),
1963                           dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
1964             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", 'u',
1965                           HELPCTX(ssh_auth_changeuser),
1966                           dlg_stdcheckbox_handler,
1967                           I(offsetof(Config,change_username)));
1968             ctrl_filesel(s, "Private key file for authentication:", 'k',
1969                          FILTER_KEY_FILES, FALSE, "Select private key file",
1970                          HELPCTX(ssh_auth_privkey),
1971                          dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
1972         }
1973
1974         if (!midsession) {
1975             /*
1976              * The Connection/SSH/TTY panel.
1977              */
1978             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
1979
1980             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
1981             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
1982                           HELPCTX(ssh_nopty),
1983                           dlg_stdcheckbox_handler,
1984                           I(offsetof(Config,nopty)));
1985
1986             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
1987                             "Terminal modes");
1988             td = (struct ttymodes_data *)
1989                 ctrl_alloc(b, sizeof(struct ttymodes_data));
1990             ctrl_columns(s, 2, 75, 25);
1991             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
1992             c->generic.column = 0;
1993             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1994                                             HELPCTX(ssh_ttymodes),
1995                                             ttymodes_handler, P(td));
1996             td->rembutton->generic.column = 1;
1997             td->rembutton->generic.tabdelay = 1;
1998             ctrl_columns(s, 1, 100);
1999             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2000                                        HELPCTX(ssh_ttymodes),
2001                                        ttymodes_handler, P(td));
2002             td->listbox->listbox.multisel = 1;
2003             td->listbox->listbox.height = 4;
2004             td->listbox->listbox.ncols = 2;
2005             td->listbox->listbox.percentages = snewn(2, int);
2006             td->listbox->listbox.percentages[0] = 40;
2007             td->listbox->listbox.percentages[1] = 60;
2008             ctrl_tabdelay(s, td->rembutton);
2009             ctrl_columns(s, 2, 75, 25);
2010             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2011                                          HELPCTX(ssh_ttymodes),
2012                                          ttymodes_handler, P(td));
2013             td->modelist->generic.column = 0;
2014             td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2015                                             HELPCTX(ssh_ttymodes),
2016                                             ttymodes_handler, P(td));
2017             td->addbutton->generic.column = 1;
2018             td->addbutton->generic.tabdelay = 1;
2019             ctrl_columns(s, 1, 100);        /* column break */
2020             /* Bit of a hack to get the value radio buttons and
2021              * edit-box on the same row. */
2022             ctrl_columns(s, 3, 25, 50, 25);
2023             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2024             c->generic.column = 0;
2025             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2026                                              HELPCTX(ssh_ttymodes),
2027                                              ttymodes_handler, P(td),
2028                                              "Auto", NO_SHORTCUT, P(NULL),
2029                                              "This:", NO_SHORTCUT, P(NULL),
2030                                              NULL);
2031             td->valradio->generic.column = 1;
2032             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2033                                       HELPCTX(ssh_ttymodes),
2034                                       ttymodes_handler, P(td), P(NULL));
2035             td->valbox->generic.column = 2;
2036             ctrl_tabdelay(s, td->addbutton);
2037
2038         }
2039
2040         if (!midsession) {
2041             /*
2042              * The Connection/SSH/X11 panel.
2043              */
2044             ctrl_settitle(b, "Connection/SSH/X11",
2045                           "Options controlling SSH X11 forwarding");
2046
2047             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2048             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2049                           HELPCTX(ssh_tunnels_x11),
2050                           dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
2051             ctrl_editbox(s, "X display location", 'x', 50,
2052                          HELPCTX(ssh_tunnels_x11),
2053                          dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
2054                          I(sizeof(((Config *)0)->x11_display)));
2055             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2056                               HELPCTX(ssh_tunnels_x11auth),
2057                               dlg_stdradiobutton_handler,
2058                               I(offsetof(Config, x11_auth)),
2059                               "MIT-Magic-Cookie-1", I(X11_MIT),
2060                               "XDM-Authorization-1", I(X11_XDM), NULL);
2061         }
2062
2063         /*
2064          * The Tunnels panel _is_ still available in mid-session.
2065          */
2066         ctrl_settitle(b, "Connection/SSH/Tunnels",
2067                       "Options controlling SSH port forwarding");
2068
2069         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2070                         "Port forwarding");
2071         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2072                       HELPCTX(ssh_tunnels_portfwd_localhost),
2073                       dlg_stdcheckbox_handler,
2074                       I(offsetof(Config,lport_acceptall)));
2075         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2076                       HELPCTX(ssh_tunnels_portfwd_localhost),
2077                       dlg_stdcheckbox_handler,
2078                       I(offsetof(Config,rport_acceptall)));
2079
2080         ctrl_columns(s, 3, 55, 20, 25);
2081         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2082         c->generic.column = COLUMN_FIELD(0,2);
2083         /* You want to select from the list, _then_ hit Remove. So tab order
2084          * should be that way round. */
2085         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2086         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2087                                          HELPCTX(ssh_tunnels_portfwd),
2088                                          portfwd_handler, P(pfd));
2089         pfd->rembutton->generic.column = 2;
2090         pfd->rembutton->generic.tabdelay = 1;
2091         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2092                                     HELPCTX(ssh_tunnels_portfwd),
2093                                     portfwd_handler, P(pfd));
2094         pfd->listbox->listbox.height = 3;
2095         pfd->listbox->listbox.ncols = 2;
2096         pfd->listbox->listbox.percentages = snewn(2, int);
2097         pfd->listbox->listbox.percentages[0] = 20;
2098         pfd->listbox->listbox.percentages[1] = 80;
2099         ctrl_tabdelay(s, pfd->rembutton);
2100         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2101         /* You want to enter source, destination and type, _then_ hit Add.
2102          * Again, we adjust the tab order to reflect this. */
2103         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2104                                          HELPCTX(ssh_tunnels_portfwd),
2105                                          portfwd_handler, P(pfd));
2106         pfd->addbutton->generic.column = 2;
2107         pfd->addbutton->generic.tabdelay = 1;
2108         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2109                                       HELPCTX(ssh_tunnels_portfwd),
2110                                       portfwd_handler, P(pfd), P(NULL));
2111         pfd->sourcebox->generic.column = 0;
2112         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2113                                     HELPCTX(ssh_tunnels_portfwd),
2114                                     portfwd_handler, P(pfd), P(NULL));
2115         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2116                                            HELPCTX(ssh_tunnels_portfwd),
2117                                            portfwd_handler, P(pfd),
2118                                            "Local", 'l', P(NULL),
2119                                            "Remote", 'm', P(NULL),
2120                                            "Dynamic", 'y', P(NULL),
2121                                            NULL);
2122 #ifndef NO_IPV6
2123         pfd->addressfamily =
2124             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2125                               HELPCTX(ssh_tunnels_portfwd_ipversion),
2126                               portfwd_handler, P(pfd),
2127                               "Auto", 'u', I(ADDRTYPE_UNSPEC),
2128                               "IPv4", '4', I(ADDRTYPE_IPV4),
2129                               "IPv6", '6', I(ADDRTYPE_IPV6),
2130                               NULL);
2131 #endif
2132         ctrl_tabdelay(s, pfd->addbutton);
2133         ctrl_columns(s, 1, 100);
2134
2135         if (!midsession) {
2136             /*
2137              * The Connection/SSH/Bugs panel.
2138              */
2139             ctrl_settitle(b, "Connection/SSH/Bugs",
2140                           "Workarounds for SSH server bugs");
2141
2142             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2143                             "Detection of known bugs in SSH servers");
2144             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2145                           HELPCTX(ssh_bugs_ignore1),
2146                           sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
2147             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2148                           HELPCTX(ssh_bugs_plainpw1),
2149                           sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
2150             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2151                           HELPCTX(ssh_bugs_rsa1),
2152                           sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
2153             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2154                           HELPCTX(ssh_bugs_hmac2),
2155                           sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
2156             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2157                           HELPCTX(ssh_bugs_derivekey2),
2158                           sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
2159             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2160                           HELPCTX(ssh_bugs_rsapad2),
2161                           sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
2162             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2163                           HELPCTX(ssh_bugs_pksessid2),
2164                           sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
2165             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2166                           HELPCTX(ssh_bugs_rekey2),
2167                           sshbug_handler, I(offsetof(Config,sshbug_rekey2)));
2168         }
2169     }
2170 }