]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Add some CHM-related stuff to the release checklist. Don't want to
[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_checkbox(s, "Disable remote window title querying (SECURITY)",
1367                   'q', HELPCTX(features_qtitle), dlg_stdcheckbox_handler,
1368                   I(offsetof(Config,no_remote_qtitle)));
1369     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1370                   HELPCTX(features_dbackspace),
1371                   dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
1372     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1373                   'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
1374                   I(offsetof(Config,no_remote_charset)));
1375     ctrl_checkbox(s, "Disable Arabic text shaping",
1376                   'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,
1377                   I(offsetof(Config, arabicshaping)));
1378     ctrl_checkbox(s, "Disable bidirectional text display",
1379                   'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,
1380                   I(offsetof(Config, bidi)));
1381
1382     /*
1383      * The Window panel.
1384      */
1385     str = dupprintf("Options controlling %s's window", appname);
1386     ctrl_settitle(b, "Window", str);
1387     sfree(str);
1388
1389     s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1390     ctrl_columns(s, 2, 50, 50);
1391     c = ctrl_editbox(s, "Rows", 'r', 100,
1392                      HELPCTX(window_size),
1393                      dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
1394     c->generic.column = 0;
1395     c = ctrl_editbox(s, "Columns", 'm', 100,
1396                      HELPCTX(window_size),
1397                      dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
1398     c->generic.column = 1;
1399     ctrl_columns(s, 1, 100);
1400
1401     s = ctrl_getset(b, "Window", "scrollback",
1402                     "Control the scrollback in the window");
1403     ctrl_editbox(s, "Lines of scrollback", 's', 50,
1404                  HELPCTX(window_scrollback),
1405                  dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
1406     ctrl_checkbox(s, "Display scrollbar", 'd',
1407                   HELPCTX(window_scrollback),
1408                   dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
1409     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1410                   HELPCTX(window_scrollback),
1411                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
1412     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1413                   HELPCTX(window_scrollback),
1414                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
1415     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1416                   HELPCTX(window_erased),
1417                   dlg_stdcheckbox_handler,
1418                   I(offsetof(Config,erase_to_scrollback)));
1419
1420     /*
1421      * The Window/Appearance panel.
1422      */
1423     str = dupprintf("Configure the appearance of %s's window", appname);
1424     ctrl_settitle(b, "Window/Appearance", str);
1425     sfree(str);
1426
1427     s = ctrl_getset(b, "Window/Appearance", "cursor",
1428                     "Adjust the use of the cursor");
1429     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1430                       HELPCTX(appearance_cursor),
1431                       dlg_stdradiobutton_handler,
1432                       I(offsetof(Config, cursor_type)),
1433                       "Block", 'l', I(0),
1434                       "Underline", 'u', I(1),
1435                       "Vertical line", 'v', I(2), NULL);
1436     ctrl_checkbox(s, "Cursor blinks", 'b',
1437                   HELPCTX(appearance_cursor),
1438                   dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
1439
1440     s = ctrl_getset(b, "Window/Appearance", "font",
1441                     "Font settings");
1442     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1443                  HELPCTX(appearance_font),
1444                  dlg_stdfontsel_handler, I(offsetof(Config, font)));
1445
1446     s = ctrl_getset(b, "Window/Appearance", "mouse",
1447                     "Adjust the use of the mouse pointer");
1448     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1449                   HELPCTX(appearance_hidemouse),
1450                   dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
1451
1452     s = ctrl_getset(b, "Window/Appearance", "border",
1453                     "Adjust the window border");
1454     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1455                  HELPCTX(appearance_border),
1456                  dlg_stdeditbox_handler,
1457                  I(offsetof(Config,window_border)), I(-1));
1458
1459     /*
1460      * The Window/Behaviour panel.
1461      */
1462     str = dupprintf("Configure the behaviour of %s's window", appname);
1463     ctrl_settitle(b, "Window/Behaviour", str);
1464     sfree(str);
1465
1466     s = ctrl_getset(b, "Window/Behaviour", "title",
1467                     "Adjust the behaviour of the window title");
1468     ctrl_editbox(s, "Window title:", 't', 100,
1469                  HELPCTX(appearance_title),
1470                  dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
1471                  I(sizeof(((Config *)0)->wintitle)));
1472     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1473                   HELPCTX(appearance_title),
1474                   dlg_stdcheckbox_handler,
1475                   I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
1476
1477     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1478     ctrl_checkbox(s, "Warn before closing window", 'w',
1479                   HELPCTX(behaviour_closewarn),
1480                   dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
1481
1482     /*
1483      * The Window/Translation panel.
1484      */
1485     ctrl_settitle(b, "Window/Translation",
1486                   "Options controlling character set translation");
1487
1488     s = ctrl_getset(b, "Window/Translation", "trans",
1489                     "Character set translation on received data");
1490     ctrl_combobox(s, "Received data assumed to be in which character set:",
1491                   'r', 100, HELPCTX(translation_codepage),
1492                   codepage_handler, P(NULL), P(NULL));
1493
1494     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1495     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1496                   HELPCTX(translation_cjk_ambig_wide),
1497                   dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide)));
1498
1499     str = dupprintf("Adjust how %s handles line drawing characters", appname);
1500     s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1501     sfree(str);
1502     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1503                       HELPCTX(translation_linedraw),
1504                       dlg_stdradiobutton_handler,
1505                       I(offsetof(Config, vtmode)),
1506                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1507                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1508                       NULL);
1509     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1510                   HELPCTX(selection_linedraw),
1511                   dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
1512
1513     /*
1514      * The Window/Selection panel.
1515      */
1516     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1517         
1518     s = ctrl_getset(b, "Window/Selection", "mouse",
1519                     "Control use of mouse");
1520     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1521                   HELPCTX(selection_shiftdrag),
1522                   dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
1523     ctrl_radiobuttons(s,
1524                       "Default selection mode (Alt+drag does the other one):",
1525                       NO_SHORTCUT, 2,
1526                       HELPCTX(selection_rect),
1527                       dlg_stdradiobutton_handler,
1528                       I(offsetof(Config, rect_select)),
1529                       "Normal", 'n', I(0),
1530                       "Rectangular block", 'r', I(1), NULL);
1531
1532     s = ctrl_getset(b, "Window/Selection", "charclass",
1533                     "Control the select-one-word-at-a-time mode");
1534     ccd = (struct charclass_data *)
1535         ctrl_alloc(b, sizeof(struct charclass_data));
1536     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1537                                 HELPCTX(selection_charclasses),
1538                                 charclass_handler, P(ccd));
1539     ccd->listbox->listbox.multisel = 1;
1540     ccd->listbox->listbox.ncols = 4;
1541     ccd->listbox->listbox.percentages = snewn(4, int);
1542     ccd->listbox->listbox.percentages[0] = 15;
1543     ccd->listbox->listbox.percentages[1] = 25;
1544     ccd->listbox->listbox.percentages[2] = 20;
1545     ccd->listbox->listbox.percentages[3] = 40;
1546     ctrl_columns(s, 2, 67, 33);
1547     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1548                                 HELPCTX(selection_charclasses),
1549                                 charclass_handler, P(ccd), P(NULL));
1550     ccd->editbox->generic.column = 0;
1551     ccd->button = ctrl_pushbutton(s, "Set", 's',
1552                                   HELPCTX(selection_charclasses),
1553                                   charclass_handler, P(ccd));
1554     ccd->button->generic.column = 1;
1555     ctrl_columns(s, 1, 100);
1556
1557     /*
1558      * The Window/Colours panel.
1559      */
1560     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1561
1562     s = ctrl_getset(b, "Window/Colours", "general",
1563                     "General options for colour usage");
1564     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1565                   HELPCTX(colours_ansi),
1566                   dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));
1567     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1568                   HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,
1569                   I(offsetof(Config,xterm_256_colour)));
1570     ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1571                   HELPCTX(colours_bold),
1572                   dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
1573
1574     str = dupprintf("Adjust the precise colours %s displays", appname);
1575     s = ctrl_getset(b, "Window/Colours", "adjust", str);
1576     sfree(str);
1577     ctrl_text(s, "Select a colour from the list, and then click the"
1578               " Modify button to change its appearance.",
1579               HELPCTX(colours_config));
1580     ctrl_columns(s, 2, 67, 33);
1581     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1582     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1583                                HELPCTX(colours_config), colour_handler, P(cd));
1584     cd->listbox->generic.column = 0;
1585     cd->listbox->listbox.height = 7;
1586     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1587     c->generic.column = 1;
1588     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1589                              colour_handler, P(cd), P(NULL));
1590     cd->redit->generic.column = 1;
1591     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1592                              colour_handler, P(cd), P(NULL));
1593     cd->gedit->generic.column = 1;
1594     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1595                              colour_handler, P(cd), P(NULL));
1596     cd->bedit->generic.column = 1;
1597     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1598                                  colour_handler, P(cd));
1599     cd->button->generic.column = 1;
1600     ctrl_columns(s, 1, 100);
1601
1602     /*
1603      * The Connection panel. This doesn't show up if we're in a
1604      * non-network utility such as pterm. We tell this by being
1605      * passed a protocol < 0.
1606      */
1607     if (protocol >= 0) {
1608         ctrl_settitle(b, "Connection", "Options controlling the connection");
1609
1610         s = ctrl_getset(b, "Connection", "keepalive",
1611                         "Sending of null packets to keep session active");
1612         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1613                      HELPCTX(connection_keepalive),
1614                      dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
1615                      I(-1));
1616
1617         if (!midsession) {
1618             s = ctrl_getset(b, "Connection", "tcp",
1619                             "Low-level TCP connection options");
1620             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1621                           'n', HELPCTX(connection_nodelay),
1622                           dlg_stdcheckbox_handler,
1623                           I(offsetof(Config,tcp_nodelay)));
1624             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1625                           'p', HELPCTX(connection_tcpkeepalive),
1626                           dlg_stdcheckbox_handler,
1627                           I(offsetof(Config,tcp_keepalives)));
1628 #ifndef NO_IPV6
1629             s = ctrl_getset(b, "Connection", "ipversion",
1630                           "Internet protocol version");
1631             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1632                           HELPCTX(connection_ipversion),
1633                           dlg_stdradiobutton_handler,
1634                           I(offsetof(Config, addressfamily)),
1635                           "Auto", 'u', I(ADDRTYPE_UNSPEC),
1636                           "IPv4", '4', I(ADDRTYPE_IPV4),
1637                           "IPv6", '6', I(ADDRTYPE_IPV6),
1638                           NULL);
1639 #endif
1640         }
1641
1642         /*
1643          * A sub-panel Connection/Data, containing options that
1644          * decide on data to send to the server.
1645          */
1646         if (!midsession) {
1647             ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1648
1649             s = ctrl_getset(b, "Connection/Data", "login",
1650                             "Login details");
1651             ctrl_editbox(s, "Auto-login username", 'u', 50,
1652                          HELPCTX(connection_username),
1653                          dlg_stdeditbox_handler, I(offsetof(Config,username)),
1654                          I(sizeof(((Config *)0)->username)));
1655
1656             s = ctrl_getset(b, "Connection/Data", "term",
1657                             "Terminal details");
1658             ctrl_editbox(s, "Terminal-type string", 't', 50,
1659                          HELPCTX(connection_termtype),
1660                          dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
1661                          I(sizeof(((Config *)0)->termtype)));
1662             ctrl_editbox(s, "Terminal speeds", 's', 50,
1663                          HELPCTX(connection_termspeed),
1664                          dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1665                          I(sizeof(((Config *)0)->termspeed)));
1666
1667             s = ctrl_getset(b, "Connection/Data", "env",
1668                             "Environment variables");
1669             ctrl_columns(s, 2, 80, 20);
1670             ed = (struct environ_data *)
1671                 ctrl_alloc(b, sizeof(struct environ_data));
1672             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1673                                       HELPCTX(telnet_environ),
1674                                       environ_handler, P(ed), P(NULL));
1675             ed->varbox->generic.column = 0;
1676             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1677                                       HELPCTX(telnet_environ),
1678                                       environ_handler, P(ed), P(NULL));
1679             ed->valbox->generic.column = 0;
1680             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1681                                             HELPCTX(telnet_environ),
1682                                             environ_handler, P(ed));
1683             ed->addbutton->generic.column = 1;
1684             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1685                                             HELPCTX(telnet_environ),
1686                                             environ_handler, P(ed));
1687             ed->rembutton->generic.column = 1;
1688             ctrl_columns(s, 1, 100);
1689             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1690                                        HELPCTX(telnet_environ),
1691                                        environ_handler, P(ed));
1692             ed->listbox->listbox.height = 3;
1693             ed->listbox->listbox.ncols = 2;
1694             ed->listbox->listbox.percentages = snewn(2, int);
1695             ed->listbox->listbox.percentages[0] = 30;
1696             ed->listbox->listbox.percentages[1] = 70;
1697         }
1698
1699     }
1700
1701     if (!midsession) {
1702         /*
1703          * The Connection/Proxy panel.
1704          */
1705         ctrl_settitle(b, "Connection/Proxy",
1706                       "Options controlling proxy usage");
1707
1708         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1709         ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1710                           HELPCTX(proxy_type),
1711                           dlg_stdradiobutton_handler,
1712                           I(offsetof(Config, proxy_type)),
1713                           "None", I(PROXY_NONE),
1714                           "SOCKS 4", I(PROXY_SOCKS4),
1715                           "SOCKS 5", I(PROXY_SOCKS5),
1716                           "HTTP", I(PROXY_HTTP),
1717                           "Telnet", I(PROXY_TELNET),
1718                           NULL);
1719         ctrl_columns(s, 2, 80, 20);
1720         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1721                          HELPCTX(proxy_main),
1722                          dlg_stdeditbox_handler,
1723                          I(offsetof(Config,proxy_host)),
1724                          I(sizeof(((Config *)0)->proxy_host)));
1725         c->generic.column = 0;
1726         c = ctrl_editbox(s, "Port", 'p', 100,
1727                          HELPCTX(proxy_main),
1728                          dlg_stdeditbox_handler,
1729                          I(offsetof(Config,proxy_port)),
1730                          I(-1));
1731         c->generic.column = 1;
1732         ctrl_columns(s, 1, 100);
1733         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1734                      HELPCTX(proxy_exclude),
1735                      dlg_stdeditbox_handler,
1736                      I(offsetof(Config,proxy_exclude_list)),
1737                      I(sizeof(((Config *)0)->proxy_exclude_list)));
1738         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1739                       HELPCTX(proxy_exclude),
1740                       dlg_stdcheckbox_handler,
1741                       I(offsetof(Config,even_proxy_localhost)));
1742         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1743                           HELPCTX(proxy_dns),
1744                           dlg_stdradiobutton_handler,
1745                           I(offsetof(Config, proxy_dns)),
1746                           "No", I(FORCE_OFF),
1747                           "Auto", I(AUTO),
1748                           "Yes", I(FORCE_ON), NULL);
1749         ctrl_editbox(s, "Username", 'u', 60,
1750                      HELPCTX(proxy_auth),
1751                      dlg_stdeditbox_handler,
1752                      I(offsetof(Config,proxy_username)),
1753                      I(sizeof(((Config *)0)->proxy_username)));
1754         c = ctrl_editbox(s, "Password", 'w', 60,
1755                          HELPCTX(proxy_auth),
1756                          dlg_stdeditbox_handler,
1757                          I(offsetof(Config,proxy_password)),
1758                          I(sizeof(((Config *)0)->proxy_password)));
1759         c->editbox.password = 1;
1760         ctrl_editbox(s, "Telnet command", 'm', 100,
1761                      HELPCTX(proxy_command),
1762                      dlg_stdeditbox_handler,
1763                      I(offsetof(Config,proxy_telnet_command)),
1764                      I(sizeof(((Config *)0)->proxy_telnet_command)));
1765     }
1766
1767     /*
1768      * The Telnet panel exists in the base config box, and in a
1769      * mid-session reconfig box _if_ we're using Telnet.
1770      */
1771     if (!midsession || protocol == PROT_TELNET) {
1772         /*
1773          * The Connection/Telnet panel.
1774          */
1775         ctrl_settitle(b, "Connection/Telnet",
1776                       "Options controlling Telnet connections");
1777
1778         s = ctrl_getset(b, "Connection/Telnet", "protocol",
1779                         "Telnet protocol adjustments");
1780
1781         if (!midsession) {
1782             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
1783                               NO_SHORTCUT, 2,
1784                               HELPCTX(telnet_oldenviron),
1785                               dlg_stdradiobutton_handler,
1786                               I(offsetof(Config, rfc_environ)),
1787                               "BSD (commonplace)", 'b', I(0),
1788                               "RFC 1408 (unusual)", 'f', I(1), NULL);
1789             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
1790                               HELPCTX(telnet_passive),
1791                               dlg_stdradiobutton_handler,
1792                               I(offsetof(Config, passive_telnet)),
1793                               "Passive", I(1), "Active", I(0), NULL);
1794         }
1795         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
1796                       HELPCTX(telnet_specialkeys),
1797                       dlg_stdcheckbox_handler,
1798                       I(offsetof(Config,telnet_keyboard)));
1799         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
1800                       'm', HELPCTX(telnet_newline),
1801                       dlg_stdcheckbox_handler,
1802                       I(offsetof(Config,telnet_newline)));
1803     }
1804
1805     if (!midsession) {
1806
1807         /*
1808          * The Connection/Rlogin panel.
1809          */
1810         ctrl_settitle(b, "Connection/Rlogin",
1811                       "Options controlling Rlogin connections");
1812
1813         s = ctrl_getset(b, "Connection/Rlogin", "data",
1814                         "Data to send to the server");
1815         ctrl_editbox(s, "Local username:", 'l', 50,
1816                      HELPCTX(rlogin_localuser),
1817                      dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
1818                      I(sizeof(((Config *)0)->localusername)));
1819
1820     }
1821
1822     /*
1823      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
1824      * when we're not doing SSH.
1825      */
1826
1827     if (backends[3].name != NULL && (!midsession || protocol == PROT_SSH)) {
1828
1829         /*
1830          * The Connection/SSH panel.
1831          */
1832         ctrl_settitle(b, "Connection/SSH",
1833                       "Options controlling SSH connections");
1834
1835         if (midsession && protcfginfo == 1) {
1836             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
1837             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
1838                       "session; it is only here so that sub-panels of it can "
1839                       "exist without looking strange.", HELPCTX(no_help));
1840         }
1841
1842         if (!midsession) {
1843
1844             s = ctrl_getset(b, "Connection/SSH", "data",
1845                             "Data to send to the server");
1846             ctrl_editbox(s, "Remote command:", 'r', 100,
1847                          HELPCTX(ssh_command),
1848                          dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
1849                          I(sizeof(((Config *)0)->remote_cmd)));
1850
1851             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1852             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
1853                           HELPCTX(ssh_noshell),
1854                           dlg_stdcheckbox_handler,
1855                           I(offsetof(Config,ssh_no_shell)));
1856         }
1857
1858         if (!midsession || protcfginfo != 1) {
1859             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1860
1861             ctrl_checkbox(s, "Enable compression", 'e',
1862                           HELPCTX(ssh_compress),
1863                           dlg_stdcheckbox_handler,
1864                           I(offsetof(Config,compression)));
1865         }
1866
1867         if (!midsession) {
1868             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1869
1870             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
1871                               HELPCTX(ssh_protocol),
1872                               dlg_stdradiobutton_handler,
1873                               I(offsetof(Config, sshprot)),
1874                               "1 only", 'l', I(0),
1875                               "1", '1', I(1),
1876                               "2", '2', I(2),
1877                               "2 only", 'y', I(3), NULL);
1878         }
1879
1880         if (!midsession || protcfginfo != 1) {
1881             s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
1882             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
1883                               HELPCTX(ssh_ciphers),
1884                               cipherlist_handler, P(NULL));
1885             c->listbox.height = 6;
1886
1887             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
1888                           HELPCTX(ssh_ciphers),
1889                           dlg_stdcheckbox_handler,
1890                           I(offsetof(Config,ssh2_des_cbc)));
1891         }
1892
1893         /*
1894          * The Connection/SSH/Kex panel. (Owing to repeat key
1895          * exchange, this is all meaningful in mid-session _if_
1896          * we're using SSH-2 or haven't decided yet.)
1897          */
1898         if (protcfginfo != 1) {
1899             ctrl_settitle(b, "Connection/SSH/Kex",
1900                           "Options controlling SSH key exchange");
1901
1902             s = ctrl_getset(b, "Connection/SSH/Kex", "main",
1903                             "Key exchange algorithm options");
1904             c = ctrl_draglist(s, "Algorithm selection policy:", 's',
1905                               HELPCTX(ssh_kexlist),
1906                               kexlist_handler, P(NULL));
1907             c->listbox.height = 5;
1908
1909             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
1910                             "Options controlling key re-exchange");
1911
1912             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
1913                          HELPCTX(ssh_kex_repeat),
1914                          dlg_stdeditbox_handler,
1915                          I(offsetof(Config,ssh_rekey_time)),
1916                          I(-1));
1917             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
1918                          HELPCTX(ssh_kex_repeat),
1919                          dlg_stdeditbox_handler,
1920                          I(offsetof(Config,ssh_rekey_data)),
1921                          I(16));
1922             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
1923                       HELPCTX(ssh_kex_repeat));
1924         }
1925
1926         if (!midsession) {
1927
1928             /*
1929              * The Connection/SSH/Auth panel.
1930              */
1931             ctrl_settitle(b, "Connection/SSH/Auth",
1932                           "Options controlling SSH authentication");
1933
1934             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
1935             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
1936                           HELPCTX(ssh_auth_bypass),
1937                           dlg_stdcheckbox_handler,
1938                           I(offsetof(Config,ssh_no_userauth)));
1939
1940             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
1941                             "Authentication methods");
1942             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
1943                           HELPCTX(ssh_auth_pageant),
1944                           dlg_stdcheckbox_handler,
1945                           I(offsetof(Config,tryagent)));
1946             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
1947                           HELPCTX(ssh_auth_tis),
1948                           dlg_stdcheckbox_handler,
1949                           I(offsetof(Config,try_tis_auth)));
1950             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
1951                           'i', HELPCTX(ssh_auth_ki),
1952                           dlg_stdcheckbox_handler,
1953                           I(offsetof(Config,try_ki_auth)));
1954
1955             s = ctrl_getset(b, "Connection/SSH/Auth", "params",
1956                             "Authentication parameters");
1957             ctrl_checkbox(s, "Allow agent forwarding", 'f',
1958                           HELPCTX(ssh_auth_agentfwd),
1959                           dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
1960             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", 'u',
1961                           HELPCTX(ssh_auth_changeuser),
1962                           dlg_stdcheckbox_handler,
1963                           I(offsetof(Config,change_username)));
1964             ctrl_filesel(s, "Private key file for authentication:", 'k',
1965                          FILTER_KEY_FILES, FALSE, "Select private key file",
1966                          HELPCTX(ssh_auth_privkey),
1967                          dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
1968         }
1969
1970         if (!midsession) {
1971             /*
1972              * The Connection/SSH/TTY panel.
1973              */
1974             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
1975
1976             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
1977             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
1978                           HELPCTX(ssh_nopty),
1979                           dlg_stdcheckbox_handler,
1980                           I(offsetof(Config,nopty)));
1981
1982             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
1983                             "Terminal modes");
1984             td = (struct ttymodes_data *)
1985                 ctrl_alloc(b, sizeof(struct ttymodes_data));
1986             ctrl_columns(s, 2, 75, 25);
1987             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
1988             c->generic.column = 0;
1989             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1990                                             HELPCTX(ssh_ttymodes),
1991                                             ttymodes_handler, P(td));
1992             td->rembutton->generic.column = 1;
1993             td->rembutton->generic.tabdelay = 1;
1994             ctrl_columns(s, 1, 100);
1995             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1996                                        HELPCTX(ssh_ttymodes),
1997                                        ttymodes_handler, P(td));
1998             td->listbox->listbox.multisel = 1;
1999             td->listbox->listbox.height = 4;
2000             td->listbox->listbox.ncols = 2;
2001             td->listbox->listbox.percentages = snewn(2, int);
2002             td->listbox->listbox.percentages[0] = 40;
2003             td->listbox->listbox.percentages[1] = 60;
2004             ctrl_tabdelay(s, td->rembutton);
2005             ctrl_columns(s, 2, 75, 25);
2006             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2007                                          HELPCTX(ssh_ttymodes),
2008                                          ttymodes_handler, P(td));
2009             td->modelist->generic.column = 0;
2010             td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2011                                             HELPCTX(ssh_ttymodes),
2012                                             ttymodes_handler, P(td));
2013             td->addbutton->generic.column = 1;
2014             td->addbutton->generic.tabdelay = 1;
2015             ctrl_columns(s, 1, 100);        /* column break */
2016             /* Bit of a hack to get the value radio buttons and
2017              * edit-box on the same row. */
2018             ctrl_columns(s, 3, 25, 50, 25);
2019             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2020             c->generic.column = 0;
2021             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2022                                              HELPCTX(ssh_ttymodes),
2023                                              ttymodes_handler, P(td),
2024                                              "Auto", NO_SHORTCUT, P(NULL),
2025                                              "This:", NO_SHORTCUT, P(NULL),
2026                                              NULL);
2027             td->valradio->generic.column = 1;
2028             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2029                                       HELPCTX(ssh_ttymodes),
2030                                       ttymodes_handler, P(td), P(NULL));
2031             td->valbox->generic.column = 2;
2032             ctrl_tabdelay(s, td->addbutton);
2033
2034         }
2035
2036         if (!midsession) {
2037             /*
2038              * The Connection/SSH/X11 panel.
2039              */
2040             ctrl_settitle(b, "Connection/SSH/X11",
2041                           "Options controlling SSH X11 forwarding");
2042
2043             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2044             ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2045                           HELPCTX(ssh_tunnels_x11),
2046                           dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
2047             ctrl_editbox(s, "X display location", 'x', 50,
2048                          HELPCTX(ssh_tunnels_x11),
2049                          dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
2050                          I(sizeof(((Config *)0)->x11_display)));
2051             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2052                               HELPCTX(ssh_tunnels_x11auth),
2053                               dlg_stdradiobutton_handler,
2054                               I(offsetof(Config, x11_auth)),
2055                               "MIT-Magic-Cookie-1", I(X11_MIT),
2056                               "XDM-Authorization-1", I(X11_XDM), NULL);
2057         }
2058
2059         /*
2060          * The Tunnels panel _is_ still available in mid-session.
2061          */
2062         ctrl_settitle(b, "Connection/SSH/Tunnels",
2063                       "Options controlling SSH port forwarding");
2064
2065         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2066                         "Port forwarding");
2067         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2068                       HELPCTX(ssh_tunnels_portfwd_localhost),
2069                       dlg_stdcheckbox_handler,
2070                       I(offsetof(Config,lport_acceptall)));
2071         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2072                       HELPCTX(ssh_tunnels_portfwd_localhost),
2073                       dlg_stdcheckbox_handler,
2074                       I(offsetof(Config,rport_acceptall)));
2075
2076         ctrl_columns(s, 3, 55, 20, 25);
2077         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2078         c->generic.column = COLUMN_FIELD(0,2);
2079         /* You want to select from the list, _then_ hit Remove. So tab order
2080          * should be that way round. */
2081         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2082         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2083                                          HELPCTX(ssh_tunnels_portfwd),
2084                                          portfwd_handler, P(pfd));
2085         pfd->rembutton->generic.column = 2;
2086         pfd->rembutton->generic.tabdelay = 1;
2087         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2088                                     HELPCTX(ssh_tunnels_portfwd),
2089                                     portfwd_handler, P(pfd));
2090         pfd->listbox->listbox.height = 3;
2091         pfd->listbox->listbox.ncols = 2;
2092         pfd->listbox->listbox.percentages = snewn(2, int);
2093         pfd->listbox->listbox.percentages[0] = 20;
2094         pfd->listbox->listbox.percentages[1] = 80;
2095         ctrl_tabdelay(s, pfd->rembutton);
2096         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2097         /* You want to enter source, destination and type, _then_ hit Add.
2098          * Again, we adjust the tab order to reflect this. */
2099         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2100                                          HELPCTX(ssh_tunnels_portfwd),
2101                                          portfwd_handler, P(pfd));
2102         pfd->addbutton->generic.column = 2;
2103         pfd->addbutton->generic.tabdelay = 1;
2104         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2105                                       HELPCTX(ssh_tunnels_portfwd),
2106                                       portfwd_handler, P(pfd), P(NULL));
2107         pfd->sourcebox->generic.column = 0;
2108         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2109                                     HELPCTX(ssh_tunnels_portfwd),
2110                                     portfwd_handler, P(pfd), P(NULL));
2111         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2112                                            HELPCTX(ssh_tunnels_portfwd),
2113                                            portfwd_handler, P(pfd),
2114                                            "Local", 'l', P(NULL),
2115                                            "Remote", 'm', P(NULL),
2116                                            "Dynamic", 'y', P(NULL),
2117                                            NULL);
2118 #ifndef NO_IPV6
2119         pfd->addressfamily =
2120             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2121                               HELPCTX(ssh_tunnels_portfwd_ipversion),
2122                               portfwd_handler, P(pfd),
2123                               "Auto", 'u', I(ADDRTYPE_UNSPEC),
2124                               "IPv4", '4', I(ADDRTYPE_IPV4),
2125                               "IPv6", '6', I(ADDRTYPE_IPV6),
2126                               NULL);
2127 #endif
2128         ctrl_tabdelay(s, pfd->addbutton);
2129         ctrl_columns(s, 1, 100);
2130
2131         if (!midsession) {
2132             /*
2133              * The Connection/SSH/Bugs panel.
2134              */
2135             ctrl_settitle(b, "Connection/SSH/Bugs",
2136                           "Workarounds for SSH server bugs");
2137
2138             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2139                             "Detection of known bugs in SSH servers");
2140             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2141                           HELPCTX(ssh_bugs_ignore1),
2142                           sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
2143             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2144                           HELPCTX(ssh_bugs_plainpw1),
2145                           sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
2146             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2147                           HELPCTX(ssh_bugs_rsa1),
2148                           sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
2149             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2150                           HELPCTX(ssh_bugs_hmac2),
2151                           sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
2152             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2153                           HELPCTX(ssh_bugs_derivekey2),
2154                           sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
2155             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2156                           HELPCTX(ssh_bugs_rsapad2),
2157                           sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
2158             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2159                           HELPCTX(ssh_bugs_pksessid2),
2160                           sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
2161             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2162                           HELPCTX(ssh_bugs_rekey2),
2163                           sshbug_handler, I(offsetof(Config,sshbug_rekey2)));
2164         }
2165     }
2166 }