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