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