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