2 * config.c - the platform-independent parts of the PuTTY
13 #define PRINTER_DISABLED_STRING "None (printing disabled)"
15 #define HOST_BOX_TITLE "Host Name (or IP address)"
16 #define PORT_BOX_TITLE "Port"
18 void conf_radiobutton_handler(union control *ctrl, void *dlg,
19 void *data, int event)
22 Conf *conf = (Conf *)data;
25 * For a standard radio button set, the context parameter gives
26 * the primary key (CONF_foo), and the extra data per button
27 * gives the value the target field should take if that button
28 * is the one selected.
30 if (event == EVENT_REFRESH) {
31 int val = conf_get_int(conf, ctrl->radio.context.i);
32 for (button = 0; button < ctrl->radio.nbuttons; button++)
33 if (val == ctrl->radio.buttondata[button].i)
35 /* We expected that `break' to happen, in all circumstances. */
36 assert(button < ctrl->radio.nbuttons);
37 dlg_radiobutton_set(ctrl, dlg, button);
38 } else if (event == EVENT_VALCHANGE) {
39 button = dlg_radiobutton_get(ctrl, dlg);
40 assert(button >= 0 && button < ctrl->radio.nbuttons);
41 conf_set_int(conf, ctrl->radio.context.i,
42 ctrl->radio.buttondata[button].i);
46 #define CHECKBOX_INVERT (1<<30)
47 void conf_checkbox_handler(union control *ctrl, void *dlg,
48 void *data, int event)
51 Conf *conf = (Conf *)data;
54 * For a standard checkbox, the context parameter gives the
55 * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
57 key = ctrl->checkbox.context.i;
58 if (key & CHECKBOX_INVERT) {
59 key &= ~CHECKBOX_INVERT;
65 * C lacks a logical XOR, so the following code uses the idiom
66 * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
67 * iff exactly one of a and b is nonzero, otherwise 0.)
70 if (event == EVENT_REFRESH) {
71 int val = conf_get_int(conf, key);
72 dlg_checkbox_set(ctrl, dlg, (!val ^ !invert));
73 } else if (event == EVENT_VALCHANGE) {
74 conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert);
78 void conf_editbox_handler(union control *ctrl, void *dlg,
79 void *data, int event)
82 * The standard edit-box handler expects the main `context'
83 * field to contain the primary key. The secondary `context2'
84 * field indicates the type of this field:
86 * - if context2 > 0, the field is a string.
87 * - if context2 == -1, the field is an int and the edit box
89 * - if context2 < -1, the field is an int and the edit box is
90 * _floating_, and (-context2) gives the scale. (E.g. if
91 * context2 == -1000, then typing 1.2 into the box will set
94 int key = ctrl->editbox.context.i;
95 int length = ctrl->editbox.context2.i;
96 Conf *conf = (Conf *)data;
99 if (event == EVENT_REFRESH) {
100 char *field = conf_get_str(conf, key);
101 dlg_editbox_set(ctrl, dlg, field);
102 } else if (event == EVENT_VALCHANGE) {
103 char *field = dlg_editbox_get(ctrl, dlg);
104 conf_set_str(conf, key, field);
107 } else if (length < 0) {
108 if (event == EVENT_REFRESH) {
110 int value = conf_get_int(conf, key);
112 sprintf(str, "%d", value);
114 sprintf(str, "%g", (double)value / (double)(-length));
115 dlg_editbox_set(ctrl, dlg, str);
116 } else if (event == EVENT_VALCHANGE) {
117 char *str = dlg_editbox_get(ctrl, dlg);
119 conf_set_int(conf, key, atoi(str));
121 conf_set_int(conf, key, (int)((-length) * atof(str)));
127 void conf_filesel_handler(union control *ctrl, void *dlg,
128 void *data, int event)
130 int key = ctrl->fileselect.context.i;
131 Conf *conf = (Conf *)data;
133 if (event == EVENT_REFRESH) {
134 dlg_filesel_set(ctrl, dlg, conf_get_filename(conf, key));
135 } else if (event == EVENT_VALCHANGE) {
136 Filename *filename = dlg_filesel_get(ctrl, dlg);
137 conf_set_filename(conf, key, filename);
138 filename_free(filename);
142 void conf_fontsel_handler(union control *ctrl, void *dlg,
143 void *data, int event)
145 int key = ctrl->fontselect.context.i;
146 Conf *conf = (Conf *)data;
148 if (event == EVENT_REFRESH) {
149 dlg_fontsel_set(ctrl, dlg, conf_get_fontspec(conf, key));
150 } else if (event == EVENT_VALCHANGE) {
151 FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg);
152 conf_set_fontspec(conf, key, fontspec);
153 fontspec_free(fontspec);
157 static void config_host_handler(union control *ctrl, void *dlg,
158 void *data, int event)
160 Conf *conf = (Conf *)data;
163 * This function works just like the standard edit box handler,
164 * only it has to choose the control's label and text from two
165 * different places depending on the protocol.
167 if (event == EVENT_REFRESH) {
168 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
170 * This label text is carefully chosen to contain an n,
171 * since that's the shortcut for the host name control.
173 dlg_label_change(ctrl, dlg, "Serial line");
174 dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
176 dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
177 dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
179 } else if (event == EVENT_VALCHANGE) {
180 char *s = dlg_editbox_get(ctrl, dlg);
181 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
182 conf_set_str(conf, CONF_serline, s);
184 conf_set_str(conf, CONF_host, s);
189 static void config_port_handler(union control *ctrl, void *dlg,
190 void *data, int event)
192 Conf *conf = (Conf *)data;
196 * This function works similarly to the standard edit box handler,
197 * only it has to choose the control's label and text from two
198 * different places depending on the protocol.
200 if (event == EVENT_REFRESH) {
201 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
203 * This label text is carefully chosen to contain a p,
204 * since that's the shortcut for the port control.
206 dlg_label_change(ctrl, dlg, "Speed");
207 sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
209 dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
210 if (conf_get_int(conf, CONF_port) != 0)
211 sprintf(buf, "%d", conf_get_int(conf, CONF_port));
213 /* Display an (invalid) port of 0 as blank */
216 dlg_editbox_set(ctrl, dlg, buf);
217 } else if (event == EVENT_VALCHANGE) {
218 char *s = dlg_editbox_get(ctrl, dlg);
222 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
223 conf_set_int(conf, CONF_serspeed, i);
225 conf_set_int(conf, CONF_port, i);
230 union control *host, *port;
234 * We export this function so that platform-specific config
235 * routines can use it to conveniently identify the protocol radio
236 * buttons in order to add to them.
238 void config_protocolbuttons_handler(union control *ctrl, void *dlg,
239 void *data, int event)
242 Conf *conf = (Conf *)data;
243 struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
246 * This function works just like the standard radio-button
247 * handler, except that it also has to change the setting of
248 * the port box, and refresh both host and port boxes when. We
249 * expect the context parameter to point at a hostport
250 * structure giving the `union control's for both.
252 if (event == EVENT_REFRESH) {
253 int protocol = conf_get_int(conf, CONF_protocol);
254 for (button = 0; button < ctrl->radio.nbuttons; button++)
255 if (protocol == ctrl->radio.buttondata[button].i)
257 /* We expected that `break' to happen, in all circumstances. */
258 assert(button < ctrl->radio.nbuttons);
259 dlg_radiobutton_set(ctrl, dlg, button);
260 } else if (event == EVENT_VALCHANGE) {
261 int oldproto = conf_get_int(conf, CONF_protocol);
264 button = dlg_radiobutton_get(ctrl, dlg);
265 assert(button >= 0 && button < ctrl->radio.nbuttons);
266 newproto = ctrl->radio.buttondata[button].i;
267 conf_set_int(conf, CONF_protocol, newproto);
269 if (oldproto != newproto) {
270 Backend *ob = backend_from_proto(oldproto);
271 Backend *nb = backend_from_proto(newproto);
274 /* Iff the user hasn't changed the port from the old protocol's
275 * default, update it with the new protocol's default.
276 * (This includes a "default" of 0, implying that there is no
277 * sensible default for that protocol; in this case it's
278 * displayed as a blank.)
279 * This helps with the common case of tabbing through the
280 * controls in order and setting a non-default port before
281 * getting to the protocol; we want that non-default port
282 * to be preserved. */
283 port = conf_get_int(conf, CONF_port);
284 if (port == ob->default_port)
285 conf_set_int(conf, CONF_port, nb->default_port);
287 dlg_refresh(hp->host, dlg);
288 dlg_refresh(hp->port, dlg);
292 static void loggingbuttons_handler(union control *ctrl, void *dlg,
293 void *data, int event)
296 Conf *conf = (Conf *)data;
297 /* This function works just like the standard radio-button handler,
298 * but it has to fall back to "no logging" in situations where the
299 * configured logging type isn't applicable.
301 if (event == EVENT_REFRESH) {
302 int logtype = conf_get_int(conf, CONF_logtype);
304 for (button = 0; button < ctrl->radio.nbuttons; button++)
305 if (logtype == ctrl->radio.buttondata[button].i)
308 /* We fell off the end, so we lack the configured logging type */
309 if (button == ctrl->radio.nbuttons) {
311 conf_set_int(conf, CONF_logtype, LGTYP_NONE);
313 dlg_radiobutton_set(ctrl, dlg, button);
314 } else if (event == EVENT_VALCHANGE) {
315 button = dlg_radiobutton_get(ctrl, dlg);
316 assert(button >= 0 && button < ctrl->radio.nbuttons);
317 conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
321 static void numeric_keypad_handler(union control *ctrl, void *dlg,
322 void *data, int event)
325 Conf *conf = (Conf *)data;
327 * This function works much like the standard radio button
328 * handler, but it has to handle two fields in Conf.
330 if (event == EVENT_REFRESH) {
331 if (conf_get_int(conf, CONF_nethack_keypad))
333 else if (conf_get_int(conf, CONF_app_keypad))
337 assert(button < ctrl->radio.nbuttons);
338 dlg_radiobutton_set(ctrl, dlg, button);
339 } else if (event == EVENT_VALCHANGE) {
340 button = dlg_radiobutton_get(ctrl, dlg);
341 assert(button >= 0 && button < ctrl->radio.nbuttons);
343 conf_set_int(conf, CONF_app_keypad, FALSE);
344 conf_set_int(conf, CONF_nethack_keypad, TRUE);
346 conf_set_int(conf, CONF_app_keypad, (button != 0));
347 conf_set_int(conf, CONF_nethack_keypad, FALSE);
352 static void cipherlist_handler(union control *ctrl, void *dlg,
353 void *data, int event)
355 Conf *conf = (Conf *)data;
356 if (event == EVENT_REFRESH) {
359 static const struct { char *s; int c; } ciphers[] = {
360 { "3DES", CIPHER_3DES },
361 { "Blowfish", CIPHER_BLOWFISH },
362 { "DES", CIPHER_DES },
363 { "AES (SSH-2 only)", CIPHER_AES },
364 { "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
365 { "-- warn below here --", CIPHER_WARN }
368 /* Set up the "selected ciphers" box. */
369 /* (cipherlist assumed to contain all ciphers) */
370 dlg_update_start(ctrl, dlg);
371 dlg_listbox_clear(ctrl, dlg);
372 for (i = 0; i < CIPHER_MAX; i++) {
373 int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
376 for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
377 if (ciphers[j].c == c) {
382 dlg_listbox_addwithid(ctrl, dlg, cstr, c);
384 dlg_update_done(ctrl, dlg);
386 } else if (event == EVENT_VALCHANGE) {
389 /* Update array to match the list box. */
390 for (i=0; i < CIPHER_MAX; i++)
391 conf_set_int_int(conf, CONF_ssh_cipherlist, i,
392 dlg_listbox_getid(ctrl, dlg, i));
397 static void gsslist_handler(union control *ctrl, void *dlg,
398 void *data, int event)
400 Conf *conf = (Conf *)data;
401 if (event == EVENT_REFRESH) {
404 dlg_update_start(ctrl, dlg);
405 dlg_listbox_clear(ctrl, dlg);
406 for (i = 0; i < ngsslibs; i++) {
407 int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
408 assert(id >= 0 && id < ngsslibs);
409 dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
411 dlg_update_done(ctrl, dlg);
413 } else if (event == EVENT_VALCHANGE) {
416 /* Update array to match the list box. */
417 for (i=0; i < ngsslibs; i++)
418 conf_set_int_int(conf, CONF_ssh_gsslist, i,
419 dlg_listbox_getid(ctrl, dlg, i));
424 static void kexlist_handler(union control *ctrl, void *dlg,
425 void *data, int event)
427 Conf *conf = (Conf *)data;
428 if (event == EVENT_REFRESH) {
431 static const struct { char *s; int k; } kexes[] = {
432 { "Diffie-Hellman group 1", KEX_DHGROUP1 },
433 { "Diffie-Hellman group 14", KEX_DHGROUP14 },
434 { "Diffie-Hellman group exchange", KEX_DHGEX },
435 { "RSA-based key exchange", KEX_RSA },
436 { "ECDH key exchange", KEX_ECDH },
437 { "-- warn below here --", KEX_WARN }
440 /* Set up the "kex preference" box. */
441 /* (kexlist assumed to contain all algorithms) */
442 dlg_update_start(ctrl, dlg);
443 dlg_listbox_clear(ctrl, dlg);
444 for (i = 0; i < KEX_MAX; i++) {
445 int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
448 for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
449 if (kexes[j].k == k) {
454 dlg_listbox_addwithid(ctrl, dlg, kstr, k);
456 dlg_update_done(ctrl, dlg);
458 } else if (event == EVENT_VALCHANGE) {
461 /* Update array to match the list box. */
462 for (i=0; i < KEX_MAX; i++)
463 conf_set_int_int(conf, CONF_ssh_kexlist, i,
464 dlg_listbox_getid(ctrl, dlg, i));
468 static void printerbox_handler(union control *ctrl, void *dlg,
469 void *data, int event)
471 Conf *conf = (Conf *)data;
472 if (event == EVENT_REFRESH) {
477 dlg_update_start(ctrl, dlg);
479 * Some backends may wish to disable the drop-down list on
480 * this edit box. Be prepared for this.
482 if (ctrl->editbox.has_list) {
483 dlg_listbox_clear(ctrl, dlg);
484 dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
485 pe = printer_start_enum(&nprinters);
486 for (i = 0; i < nprinters; i++)
487 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
488 printer_finish_enum(pe);
490 printer = conf_get_str(conf, CONF_printer);
492 printer = PRINTER_DISABLED_STRING;
493 dlg_editbox_set(ctrl, dlg, printer);
494 dlg_update_done(ctrl, dlg);
495 } else if (event == EVENT_VALCHANGE) {
496 char *printer = dlg_editbox_get(ctrl, dlg);
497 if (!strcmp(printer, PRINTER_DISABLED_STRING))
499 conf_set_str(conf, CONF_printer, printer);
504 static void codepage_handler(union control *ctrl, void *dlg,
505 void *data, int event)
507 Conf *conf = (Conf *)data;
508 if (event == EVENT_REFRESH) {
510 const char *cp, *thiscp;
511 dlg_update_start(ctrl, dlg);
512 thiscp = cp_name(decode_codepage(conf_get_str(conf,
513 CONF_line_codepage)));
514 dlg_listbox_clear(ctrl, dlg);
515 for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
516 dlg_listbox_add(ctrl, dlg, cp);
517 dlg_editbox_set(ctrl, dlg, thiscp);
518 conf_set_str(conf, CONF_line_codepage, thiscp);
519 dlg_update_done(ctrl, dlg);
520 } else if (event == EVENT_VALCHANGE) {
521 char *codepage = dlg_editbox_get(ctrl, dlg);
522 conf_set_str(conf, CONF_line_codepage,
523 cp_name(decode_codepage(codepage)));
528 static void sshbug_handler(union control *ctrl, void *dlg,
529 void *data, int event)
531 Conf *conf = (Conf *)data;
532 if (event == EVENT_REFRESH) {
534 * We must fetch the previously configured value from the Conf
535 * before we start modifying the drop-down list, otherwise the
536 * spurious SELCHANGE we trigger in the process will overwrite
537 * the value we wanted to keep.
539 int oldconf = conf_get_int(conf, ctrl->listbox.context.i);
540 dlg_update_start(ctrl, dlg);
541 dlg_listbox_clear(ctrl, dlg);
542 dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
543 dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
544 dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
546 case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
547 case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
548 case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
550 dlg_update_done(ctrl, dlg);
551 } else if (event == EVENT_SELCHANGE) {
552 int i = dlg_listbox_index(ctrl, dlg);
556 i = dlg_listbox_getid(ctrl, dlg, i);
557 conf_set_int(conf, ctrl->listbox.context.i, i);
561 struct sessionsaver_data {
562 union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
563 union control *okbutton, *cancelbutton;
564 struct sesslist sesslist;
566 char *savedsession; /* the current contents of ssd->editbox */
569 static void sessionsaver_data_free(void *ssdv)
571 struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
572 sfree(ssd->savedsession);
577 * Helper function to load the session selected in the list box, if
578 * any, as this is done in more than one place below. Returns 0 for
581 static int load_selected_session(struct sessionsaver_data *ssd,
582 void *dlg, Conf *conf, int *maybe_launch)
584 int i = dlg_listbox_index(ssd->listbox, dlg);
590 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
591 load_settings(ssd->sesslist.sessions[i], conf);
592 sfree(ssd->savedsession);
593 ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]);
595 *maybe_launch = !isdef;
596 dlg_refresh(NULL, dlg);
597 /* Restore the selection, which might have been clobbered by
598 * changing the value of the edit box. */
599 dlg_listbox_select(ssd->listbox, dlg, i);
603 static void sessionsaver_handler(union control *ctrl, void *dlg,
604 void *data, int event)
606 Conf *conf = (Conf *)data;
607 struct sessionsaver_data *ssd =
608 (struct sessionsaver_data *)ctrl->generic.context.p;
610 if (event == EVENT_REFRESH) {
611 if (ctrl == ssd->editbox) {
612 dlg_editbox_set(ctrl, dlg, ssd->savedsession);
613 } else if (ctrl == ssd->listbox) {
615 dlg_update_start(ctrl, dlg);
616 dlg_listbox_clear(ctrl, dlg);
617 for (i = 0; i < ssd->sesslist.nsessions; i++)
618 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
619 dlg_update_done(ctrl, dlg);
621 } else if (event == EVENT_VALCHANGE) {
622 int top, bottom, halfway, i;
623 if (ctrl == ssd->editbox) {
624 sfree(ssd->savedsession);
625 ssd->savedsession = dlg_editbox_get(ctrl, dlg);
626 top = ssd->sesslist.nsessions;
628 while (top-bottom > 1) {
629 halfway = (top+bottom)/2;
630 i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]);
637 if (top == ssd->sesslist.nsessions) {
640 dlg_listbox_select(ssd->listbox, dlg, top);
642 } else if (event == EVENT_ACTION) {
644 if (!ssd->midsession &&
645 (ctrl == ssd->listbox ||
646 (ssd->loadbutton && ctrl == ssd->loadbutton))) {
648 * The user has double-clicked a session, or hit Load.
649 * We must load the selected session, and then
650 * terminate the configuration dialog _if_ there was a
651 * double-click on the list box _and_ that session
652 * contains a hostname.
654 if (load_selected_session(ssd, dlg, conf, &mbl) &&
655 (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
656 dlg_end(dlg, 1); /* it's all over, and succeeded */
658 } else if (ctrl == ssd->savebutton) {
659 int isdef = !strcmp(ssd->savedsession, "Default Settings");
660 if (!ssd->savedsession[0]) {
661 int i = dlg_listbox_index(ssd->listbox, dlg);
666 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
667 sfree(ssd->savedsession);
668 ssd->savedsession = dupstr(isdef ? "" :
669 ssd->sesslist.sessions[i]);
672 char *errmsg = save_settings(ssd->savedsession, conf);
674 dlg_error_msg(dlg, errmsg);
678 get_sesslist(&ssd->sesslist, FALSE);
679 get_sesslist(&ssd->sesslist, TRUE);
680 dlg_refresh(ssd->editbox, dlg);
681 dlg_refresh(ssd->listbox, dlg);
682 } else if (!ssd->midsession &&
683 ssd->delbutton && ctrl == ssd->delbutton) {
684 int i = dlg_listbox_index(ssd->listbox, dlg);
688 del_settings(ssd->sesslist.sessions[i]);
689 get_sesslist(&ssd->sesslist, FALSE);
690 get_sesslist(&ssd->sesslist, TRUE);
691 dlg_refresh(ssd->listbox, dlg);
693 } else if (ctrl == ssd->okbutton) {
694 if (ssd->midsession) {
695 /* In a mid-session Change Settings, Apply is always OK. */
700 * Annoying special case. If the `Open' button is
701 * pressed while no host name is currently set, _and_
702 * the session list previously had the focus, _and_
703 * there was a session selected in that which had a
704 * valid host name in it, then load it and go.
706 if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
707 !conf_launchable(conf)) {
708 Conf *conf2 = conf_new();
710 if (!load_selected_session(ssd, dlg, conf2, &mbl)) {
715 /* If at this point we have a valid session, go! */
716 if (mbl && conf_launchable(conf2)) {
717 conf_copy_into(conf, conf2);
727 * Otherwise, do the normal thing: if we have a valid
728 * session, get going.
730 if (conf_launchable(conf)) {
734 } else if (ctrl == ssd->cancelbutton) {
740 struct charclass_data {
741 union control *listbox, *editbox, *button;
744 static void charclass_handler(union control *ctrl, void *dlg,
745 void *data, int event)
747 Conf *conf = (Conf *)data;
748 struct charclass_data *ccd =
749 (struct charclass_data *)ctrl->generic.context.p;
751 if (event == EVENT_REFRESH) {
752 if (ctrl == ccd->listbox) {
754 dlg_update_start(ctrl, dlg);
755 dlg_listbox_clear(ctrl, dlg);
756 for (i = 0; i < 128; i++) {
758 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
759 (i >= 0x21 && i != 0x7F) ? i : ' ',
760 conf_get_int_int(conf, CONF_wordness, i));
761 dlg_listbox_add(ctrl, dlg, str);
763 dlg_update_done(ctrl, dlg);
765 } else if (event == EVENT_ACTION) {
766 if (ctrl == ccd->button) {
769 str = dlg_editbox_get(ccd->editbox, dlg);
772 for (i = 0; i < 128; i++) {
773 if (dlg_listbox_issel(ccd->listbox, dlg, i))
774 conf_set_int_int(conf, CONF_wordness, i, n);
776 dlg_refresh(ccd->listbox, dlg);
782 union control *listbox, *redit, *gedit, *bedit, *button;
785 static const char *const colours[] = {
786 "Default Foreground", "Default Bold Foreground",
787 "Default Background", "Default Bold Background",
788 "Cursor Text", "Cursor Colour",
789 "ANSI Black", "ANSI Black Bold",
790 "ANSI Red", "ANSI Red Bold",
791 "ANSI Green", "ANSI Green Bold",
792 "ANSI Yellow", "ANSI Yellow Bold",
793 "ANSI Blue", "ANSI Blue Bold",
794 "ANSI Magenta", "ANSI Magenta Bold",
795 "ANSI Cyan", "ANSI Cyan Bold",
796 "ANSI White", "ANSI White Bold"
799 static void colour_handler(union control *ctrl, void *dlg,
800 void *data, int event)
802 Conf *conf = (Conf *)data;
803 struct colour_data *cd =
804 (struct colour_data *)ctrl->generic.context.p;
805 int update = FALSE, clear = FALSE, r, g, b;
807 if (event == EVENT_REFRESH) {
808 if (ctrl == cd->listbox) {
810 dlg_update_start(ctrl, dlg);
811 dlg_listbox_clear(ctrl, dlg);
812 for (i = 0; i < lenof(colours); i++)
813 dlg_listbox_add(ctrl, dlg, colours[i]);
814 dlg_update_done(ctrl, dlg);
818 } else if (event == EVENT_SELCHANGE) {
819 if (ctrl == cd->listbox) {
820 /* The user has selected a colour. Update the RGB text. */
821 int i = dlg_listbox_index(ctrl, dlg);
826 r = conf_get_int_int(conf, CONF_colours, i*3+0);
827 g = conf_get_int_int(conf, CONF_colours, i*3+1);
828 b = conf_get_int_int(conf, CONF_colours, i*3+2);
832 } else if (event == EVENT_VALCHANGE) {
833 if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
834 /* The user has changed the colour using the edit boxes. */
838 str = dlg_editbox_get(ctrl, dlg);
841 if (cval > 255) cval = 255;
842 if (cval < 0) cval = 0;
844 i = dlg_listbox_index(cd->listbox, dlg);
846 if (ctrl == cd->redit)
847 conf_set_int_int(conf, CONF_colours, i*3+0, cval);
848 else if (ctrl == cd->gedit)
849 conf_set_int_int(conf, CONF_colours, i*3+1, cval);
850 else if (ctrl == cd->bedit)
851 conf_set_int_int(conf, CONF_colours, i*3+2, cval);
854 } else if (event == EVENT_ACTION) {
855 if (ctrl == cd->button) {
856 int i = dlg_listbox_index(cd->listbox, dlg);
862 * Start a colour selector, which will send us an
863 * EVENT_CALLBACK when it's finished and allow us to
864 * pick up the results.
866 dlg_coloursel_start(ctrl, dlg,
867 conf_get_int_int(conf, CONF_colours, i*3+0),
868 conf_get_int_int(conf, CONF_colours, i*3+1),
869 conf_get_int_int(conf, CONF_colours, i*3+2));
871 } else if (event == EVENT_CALLBACK) {
872 if (ctrl == cd->button) {
873 int i = dlg_listbox_index(cd->listbox, dlg);
875 * Collect the results of the colour selector. Will
876 * return nonzero on success, or zero if the colour
877 * selector did nothing (user hit Cancel, for example).
879 if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
880 conf_set_int_int(conf, CONF_colours, i*3+0, r);
881 conf_set_int_int(conf, CONF_colours, i*3+1, g);
882 conf_set_int_int(conf, CONF_colours, i*3+2, b);
891 dlg_editbox_set(cd->redit, dlg, "");
892 dlg_editbox_set(cd->gedit, dlg, "");
893 dlg_editbox_set(cd->bedit, dlg, "");
896 sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
897 sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
898 sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
903 struct ttymodes_data {
904 union control *modelist, *valradio, *valbox;
905 union control *addbutton, *rembutton, *listbox;
908 static void ttymodes_handler(union control *ctrl, void *dlg,
909 void *data, int event)
911 Conf *conf = (Conf *)data;
912 struct ttymodes_data *td =
913 (struct ttymodes_data *)ctrl->generic.context.p;
915 if (event == EVENT_REFRESH) {
916 if (ctrl == td->listbox) {
918 dlg_update_start(ctrl, dlg);
919 dlg_listbox_clear(ctrl, dlg);
920 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
922 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
923 char *disp = dupprintf("%s\t%s", key,
924 (val[0] == 'A') ? "(auto)" : val+1);
925 dlg_listbox_add(ctrl, dlg, disp);
928 dlg_update_done(ctrl, dlg);
929 } else if (ctrl == td->modelist) {
931 dlg_update_start(ctrl, dlg);
932 dlg_listbox_clear(ctrl, dlg);
933 for (i = 0; ttymodes[i]; i++)
934 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
935 dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
936 dlg_update_done(ctrl, dlg);
937 } else if (ctrl == td->valradio) {
938 dlg_radiobutton_set(ctrl, dlg, 0);
940 } else if (event == EVENT_ACTION) {
941 if (ctrl == td->addbutton) {
942 int ind = dlg_listbox_index(td->modelist, dlg);
944 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
947 /* Construct new entry */
949 str = dlg_editbox_get(td->valbox, dlg);
950 val = dupprintf("%c%s", type, str);
952 conf_set_str_str(conf, CONF_ttymodes, key, val);
954 dlg_refresh(td->listbox, dlg);
957 } else if (ctrl == td->rembutton) {
960 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
961 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
963 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
964 if (dlg_listbox_issel(td->listbox, dlg, i)) {
966 /* Populate controls with entry we're about to
967 * delete, for ease of editing.
968 * (If multiple entries were selected, don't
969 * touch the controls.) */
972 while (ttymodes[ind]) {
973 if (!strcmp(ttymodes[ind], key))
977 dlg_listbox_select(td->modelist, dlg, ind);
978 dlg_radiobutton_set(td->valradio, dlg,
980 dlg_editbox_set(td->valbox, dlg, val+1);
982 conf_del_str_str(conf, CONF_ttymodes, key);
986 dlg_refresh(td->listbox, dlg);
991 struct environ_data {
992 union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
995 static void environ_handler(union control *ctrl, void *dlg,
996 void *data, int event)
998 Conf *conf = (Conf *)data;
999 struct environ_data *ed =
1000 (struct environ_data *)ctrl->generic.context.p;
1002 if (event == EVENT_REFRESH) {
1003 if (ctrl == ed->listbox) {
1005 dlg_update_start(ctrl, dlg);
1006 dlg_listbox_clear(ctrl, dlg);
1007 for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1009 val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1010 char *p = dupprintf("%s\t%s", key, val);
1011 dlg_listbox_add(ctrl, dlg, p);
1014 dlg_update_done(ctrl, dlg);
1016 } else if (event == EVENT_ACTION) {
1017 if (ctrl == ed->addbutton) {
1018 char *key, *val, *str;
1019 key = dlg_editbox_get(ed->varbox, dlg);
1025 val = dlg_editbox_get(ed->valbox, dlg);
1032 conf_set_str_str(conf, CONF_environmt, key, val);
1033 str = dupcat(key, "\t", val, NULL);
1034 dlg_editbox_set(ed->varbox, dlg, "");
1035 dlg_editbox_set(ed->valbox, dlg, "");
1039 dlg_refresh(ed->listbox, dlg);
1040 } else if (ctrl == ed->rembutton) {
1041 int i = dlg_listbox_index(ed->listbox, dlg);
1047 key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
1049 /* Populate controls with the entry we're about to delete
1050 * for ease of editing */
1051 val = conf_get_str_str(conf, CONF_environmt, key);
1052 dlg_editbox_set(ed->varbox, dlg, key);
1053 dlg_editbox_set(ed->valbox, dlg, val);
1055 conf_del_str_str(conf, CONF_environmt, key);
1058 dlg_refresh(ed->listbox, dlg);
1063 struct portfwd_data {
1064 union control *addbutton, *rembutton, *listbox;
1065 union control *sourcebox, *destbox, *direction;
1067 union control *addressfamily;
1071 static void portfwd_handler(union control *ctrl, void *dlg,
1072 void *data, int event)
1074 Conf *conf = (Conf *)data;
1075 struct portfwd_data *pfd =
1076 (struct portfwd_data *)ctrl->generic.context.p;
1078 if (event == EVENT_REFRESH) {
1079 if (ctrl == pfd->listbox) {
1081 dlg_update_start(ctrl, dlg);
1082 dlg_listbox_clear(ctrl, dlg);
1083 for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
1085 val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
1087 if (!strcmp(val, "D")) {
1090 * A dynamic forwarding is stored as L12345=D or
1091 * 6L12345=D (since it's mutually exclusive with
1092 * L12345=anything else), but displayed as D12345
1093 * to match the fiction that 'Local', 'Remote' and
1094 * 'Dynamic' are three distinct modes and also to
1095 * align with OpenSSH's command line option syntax
1096 * that people will already be used to. So, for
1097 * display purposes, find the L in the key string
1098 * and turn it into a D.
1100 p = dupprintf("%s\t", key);
1104 p = dupprintf("%s\t%s", key, val);
1105 dlg_listbox_add(ctrl, dlg, p);
1108 dlg_update_done(ctrl, dlg);
1109 } else if (ctrl == pfd->direction) {
1113 dlg_radiobutton_set(ctrl, dlg, 0);
1115 } else if (ctrl == pfd->addressfamily) {
1116 dlg_radiobutton_set(ctrl, dlg, 0);
1119 } else if (event == EVENT_ACTION) {
1120 if (ctrl == pfd->addbutton) {
1121 char *family, *type, *src, *key, *val;
1125 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1126 if (whichbutton == 1)
1128 else if (whichbutton == 2)
1134 whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1135 if (whichbutton == 0)
1137 else if (whichbutton == 1)
1142 src = dlg_editbox_get(pfd->sourcebox, dlg);
1144 dlg_error_msg(dlg, "You need to specify a source port number");
1149 val = dlg_editbox_get(pfd->destbox, dlg);
1150 if (!*val || !host_strchr(val, ':')) {
1152 "You need to specify a destination address\n"
1153 "in the form \"host.name:port\"");
1160 val = dupstr("D"); /* special case */
1163 key = dupcat(family, type, src, NULL);
1166 if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1167 dlg_error_msg(dlg, "Specified forwarding already exists");
1169 conf_set_str_str(conf, CONF_portfwd, key, val);
1174 dlg_refresh(pfd->listbox, dlg);
1175 } else if (ctrl == pfd->rembutton) {
1176 int i = dlg_listbox_index(pfd->listbox, dlg);
1180 char *key, *val, *p;
1182 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1184 static const char *const afs = "A46";
1185 static const char *const dirs = "LRD";
1192 /* Populate controls with the entry we're about to delete
1193 * for ease of editing */
1196 afp = strchr(afs, *p);
1198 idx = afp ? afp-afs : 0;
1203 dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1208 val = conf_get_str_str(conf, CONF_portfwd, key);
1209 if (!strcmp(val, "D")) {
1214 dlg_radiobutton_set(pfd->direction, dlg,
1215 strchr(dirs, dir) - dirs);
1218 dlg_editbox_set(pfd->sourcebox, dlg, p);
1219 dlg_editbox_set(pfd->destbox, dlg, val);
1221 conf_del_str_str(conf, CONF_portfwd, key);
1224 dlg_refresh(pfd->listbox, dlg);
1229 struct manual_hostkey_data {
1230 union control *addbutton, *rembutton, *listbox, *keybox;
1233 static void manual_hostkey_handler(union control *ctrl, void *dlg,
1234 void *data, int event)
1236 Conf *conf = (Conf *)data;
1237 struct manual_hostkey_data *mh =
1238 (struct manual_hostkey_data *)ctrl->generic.context.p;
1240 if (event == EVENT_REFRESH) {
1241 if (ctrl == mh->listbox) {
1243 dlg_update_start(ctrl, dlg);
1244 dlg_listbox_clear(ctrl, dlg);
1245 for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1248 val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1250 dlg_listbox_add(ctrl, dlg, key);
1252 dlg_update_done(ctrl, dlg);
1254 } else if (event == EVENT_ACTION) {
1255 if (ctrl == mh->addbutton) {
1258 key = dlg_editbox_get(mh->keybox, dlg);
1260 dlg_error_msg(dlg, "You need to specify a host key or "
1266 if (!validate_manual_hostkey(key)) {
1267 dlg_error_msg(dlg, "Host key is not in a valid format");
1268 } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
1270 dlg_error_msg(dlg, "Specified host key is already listed");
1272 conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
1276 dlg_refresh(mh->listbox, dlg);
1277 } else if (ctrl == mh->rembutton) {
1278 int i = dlg_listbox_index(mh->listbox, dlg);
1284 key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
1286 dlg_editbox_set(mh->keybox, dlg, key);
1288 conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
1291 dlg_refresh(mh->listbox, dlg);
1296 void setup_config_box(struct controlbox *b, int midsession,
1297 int protocol, int protcfginfo)
1299 struct controlset *s;
1300 struct sessionsaver_data *ssd;
1301 struct charclass_data *ccd;
1302 struct colour_data *cd;
1303 struct ttymodes_data *td;
1304 struct environ_data *ed;
1305 struct portfwd_data *pfd;
1306 struct manual_hostkey_data *mh;
1310 ssd = (struct sessionsaver_data *)
1311 ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data),
1312 sessionsaver_data_free);
1313 memset(ssd, 0, sizeof(*ssd));
1314 ssd->savedsession = dupstr("");
1315 ssd->midsession = midsession;
1318 * The standard panel that appears at the bottom of all panels:
1319 * Open, Cancel, Apply etc.
1321 s = ctrl_getset(b, "", "", "");
1322 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1323 ssd->okbutton = ctrl_pushbutton(s,
1324 (midsession ? "Apply" : "Open"),
1325 (char)(midsession ? 'a' : 'o'),
1327 sessionsaver_handler, P(ssd));
1328 ssd->okbutton->button.isdefault = TRUE;
1329 ssd->okbutton->generic.column = 3;
1330 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1331 sessionsaver_handler, P(ssd));
1332 ssd->cancelbutton->button.iscancel = TRUE;
1333 ssd->cancelbutton->generic.column = 4;
1334 /* We carefully don't close the 5-column part, so that platform-
1335 * specific add-ons can put extra buttons alongside Open and Cancel. */
1338 * The Session panel.
1340 str = dupprintf("Basic options for your %s session", appname);
1341 ctrl_settitle(b, "Session", str);
1345 struct hostport *hp = (struct hostport *)
1346 ctrl_alloc(b, sizeof(struct hostport));
1348 s = ctrl_getset(b, "Session", "hostport",
1349 "Specify the destination you want to connect to");
1350 ctrl_columns(s, 2, 75, 25);
1351 c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1352 HELPCTX(session_hostname),
1353 config_host_handler, I(0), I(0));
1354 c->generic.column = 0;
1356 c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1357 HELPCTX(session_hostname),
1358 config_port_handler, I(0), I(0));
1359 c->generic.column = 1;
1361 ctrl_columns(s, 1, 100);
1363 if (!backend_from_proto(PROT_SSH)) {
1364 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1365 HELPCTX(session_hostname),
1366 config_protocolbuttons_handler, P(hp),
1367 "Raw", 'w', I(PROT_RAW),
1368 "Telnet", 't', I(PROT_TELNET),
1369 "Rlogin", 'i', I(PROT_RLOGIN),
1372 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1373 HELPCTX(session_hostname),
1374 config_protocolbuttons_handler, P(hp),
1375 "Raw", 'w', I(PROT_RAW),
1376 "Telnet", 't', I(PROT_TELNET),
1377 "Rlogin", 'i', I(PROT_RLOGIN),
1378 "SSH", 's', I(PROT_SSH),
1384 * The Load/Save panel is available even in mid-session.
1386 s = ctrl_getset(b, "Session", "savedsessions",
1387 midsession ? "Save the current session settings" :
1388 "Load, save or delete a stored session");
1389 ctrl_columns(s, 2, 75, 25);
1390 get_sesslist(&ssd->sesslist, TRUE);
1391 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1392 HELPCTX(session_saved),
1393 sessionsaver_handler, P(ssd), P(NULL));
1394 ssd->editbox->generic.column = 0;
1395 /* Reset columns so that the buttons are alongside the list, rather
1396 * than alongside that edit box. */
1397 ctrl_columns(s, 1, 100);
1398 ctrl_columns(s, 2, 75, 25);
1399 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1400 HELPCTX(session_saved),
1401 sessionsaver_handler, P(ssd));
1402 ssd->listbox->generic.column = 0;
1403 ssd->listbox->listbox.height = 7;
1405 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1406 HELPCTX(session_saved),
1407 sessionsaver_handler, P(ssd));
1408 ssd->loadbutton->generic.column = 1;
1410 /* We can't offer the Load button mid-session, as it would allow the
1411 * user to load and subsequently save settings they can't see. (And
1412 * also change otherwise immutable settings underfoot; that probably
1413 * shouldn't be a problem, but.) */
1414 ssd->loadbutton = NULL;
1416 /* "Save" button is permitted mid-session. */
1417 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1418 HELPCTX(session_saved),
1419 sessionsaver_handler, P(ssd));
1420 ssd->savebutton->generic.column = 1;
1422 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1423 HELPCTX(session_saved),
1424 sessionsaver_handler, P(ssd));
1425 ssd->delbutton->generic.column = 1;
1427 /* Disable the Delete button mid-session too, for UI consistency. */
1428 ssd->delbutton = NULL;
1430 ctrl_columns(s, 1, 100);
1432 s = ctrl_getset(b, "Session", "otheropts", NULL);
1433 ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1434 HELPCTX(session_coe),
1435 conf_radiobutton_handler,
1436 I(CONF_close_on_exit),
1437 "Always", I(FORCE_ON),
1438 "Never", I(FORCE_OFF),
1439 "Only on clean exit", I(AUTO), NULL);
1442 * The Session/Logging panel.
1444 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1446 s = ctrl_getset(b, "Session/Logging", "main", NULL);
1448 * The logging buttons change depending on whether SSH packet
1449 * logging can sensibly be available.
1452 char *sshlogname, *sshrawlogname;
1453 if ((midsession && protocol == PROT_SSH) ||
1454 (!midsession && backend_from_proto(PROT_SSH))) {
1455 sshlogname = "SSH packets";
1456 sshrawlogname = "SSH packets and raw data";
1458 sshlogname = NULL; /* this will disable both buttons */
1459 sshrawlogname = NULL; /* this will just placate optimisers */
1461 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1462 HELPCTX(logging_main),
1463 loggingbuttons_handler,
1465 "None", 't', I(LGTYP_NONE),
1466 "Printable output", 'p', I(LGTYP_ASCII),
1467 "All session output", 'l', I(LGTYP_DEBUG),
1468 sshlogname, 's', I(LGTYP_PACKETS),
1469 sshrawlogname, 'r', I(LGTYP_SSHRAW),
1472 ctrl_filesel(s, "Log file name:", 'f',
1473 NULL, TRUE, "Select session log file name",
1474 HELPCTX(logging_filename),
1475 conf_filesel_handler, I(CONF_logfilename));
1476 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1477 " &T for time, and &H for host name)",
1478 HELPCTX(logging_filename));
1479 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1480 HELPCTX(logging_exists),
1481 conf_radiobutton_handler, I(CONF_logxfovr),
1482 "Always overwrite it", I(LGXF_OVR),
1483 "Always append to the end of it", I(LGXF_APN),
1484 "Ask the user every time", I(LGXF_ASK), NULL);
1485 ctrl_checkbox(s, "Flush log file frequently", 'u',
1486 HELPCTX(logging_flush),
1487 conf_checkbox_handler, I(CONF_logflush));
1489 if ((midsession && protocol == PROT_SSH) ||
1490 (!midsession && backend_from_proto(PROT_SSH))) {
1491 s = ctrl_getset(b, "Session/Logging", "ssh",
1492 "Options specific to SSH packet logging");
1493 ctrl_checkbox(s, "Omit known password fields", 'k',
1494 HELPCTX(logging_ssh_omit_password),
1495 conf_checkbox_handler, I(CONF_logomitpass));
1496 ctrl_checkbox(s, "Omit session data", 'd',
1497 HELPCTX(logging_ssh_omit_data),
1498 conf_checkbox_handler, I(CONF_logomitdata));
1502 * The Terminal panel.
1504 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1506 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1507 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1508 HELPCTX(terminal_autowrap),
1509 conf_checkbox_handler, I(CONF_wrap_mode));
1510 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1511 HELPCTX(terminal_decom),
1512 conf_checkbox_handler, I(CONF_dec_om));
1513 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1514 HELPCTX(terminal_lfhascr),
1515 conf_checkbox_handler, I(CONF_lfhascr));
1516 ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1517 HELPCTX(terminal_crhaslf),
1518 conf_checkbox_handler, I(CONF_crhaslf));
1519 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1520 HELPCTX(terminal_bce),
1521 conf_checkbox_handler, I(CONF_bce));
1522 ctrl_checkbox(s, "Enable blinking text", 'n',
1523 HELPCTX(terminal_blink),
1524 conf_checkbox_handler, I(CONF_blinktext));
1525 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1526 HELPCTX(terminal_answerback),
1527 conf_editbox_handler, I(CONF_answerback), I(1));
1529 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1530 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1531 HELPCTX(terminal_localecho),
1532 conf_radiobutton_handler,I(CONF_localecho),
1534 "Force on", I(FORCE_ON),
1535 "Force off", I(FORCE_OFF), NULL);
1536 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1537 HELPCTX(terminal_localedit),
1538 conf_radiobutton_handler,I(CONF_localedit),
1540 "Force on", I(FORCE_ON),
1541 "Force off", I(FORCE_OFF), NULL);
1543 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1544 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1545 HELPCTX(terminal_printing),
1546 printerbox_handler, P(NULL), P(NULL));
1549 * The Terminal/Keyboard panel.
1551 ctrl_settitle(b, "Terminal/Keyboard",
1552 "Options controlling the effects of keys");
1554 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1555 "Change the sequences sent by:");
1556 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1557 HELPCTX(keyboard_backspace),
1558 conf_radiobutton_handler,
1559 I(CONF_bksp_is_delete),
1560 "Control-H", I(0), "Control-? (127)", I(1), NULL);
1561 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1562 HELPCTX(keyboard_homeend),
1563 conf_radiobutton_handler,
1564 I(CONF_rxvt_homeend),
1565 "Standard", I(0), "rxvt", I(1), NULL);
1566 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1567 HELPCTX(keyboard_funkeys),
1568 conf_radiobutton_handler,
1570 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1571 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1573 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1574 "Application keypad settings:");
1575 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1576 HELPCTX(keyboard_appcursor),
1577 conf_radiobutton_handler,
1579 "Normal", I(0), "Application", I(1), NULL);
1580 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1581 HELPCTX(keyboard_appkeypad),
1582 numeric_keypad_handler, P(NULL),
1583 "Normal", I(0), "Application", I(1), "NetHack", I(2),
1587 * The Terminal/Bell panel.
1589 ctrl_settitle(b, "Terminal/Bell",
1590 "Options controlling the terminal bell");
1592 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1593 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1594 HELPCTX(bell_style),
1595 conf_radiobutton_handler, I(CONF_beep),
1596 "None (bell disabled)", I(BELL_DISABLED),
1597 "Make default system alert sound", I(BELL_DEFAULT),
1598 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1600 s = ctrl_getset(b, "Terminal/Bell", "overload",
1601 "Control the bell overload behaviour");
1602 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1603 HELPCTX(bell_overload),
1604 conf_checkbox_handler, I(CONF_bellovl));
1605 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1606 HELPCTX(bell_overload),
1607 conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1608 ctrl_editbox(s, "... in this many seconds", 't', 20,
1609 HELPCTX(bell_overload),
1610 conf_editbox_handler, I(CONF_bellovl_t),
1612 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1613 HELPCTX(bell_overload));
1614 ctrl_editbox(s, "Seconds of silence required", 's', 20,
1615 HELPCTX(bell_overload),
1616 conf_editbox_handler, I(CONF_bellovl_s),
1620 * The Terminal/Features panel.
1622 ctrl_settitle(b, "Terminal/Features",
1623 "Enabling and disabling advanced terminal features");
1625 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1626 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1627 HELPCTX(features_application),
1628 conf_checkbox_handler, I(CONF_no_applic_c));
1629 ctrl_checkbox(s, "Disable application keypad mode", 'k',
1630 HELPCTX(features_application),
1631 conf_checkbox_handler, I(CONF_no_applic_k));
1632 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1633 HELPCTX(features_mouse),
1634 conf_checkbox_handler, I(CONF_no_mouse_rep));
1635 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1636 HELPCTX(features_resize),
1637 conf_checkbox_handler,
1638 I(CONF_no_remote_resize));
1639 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1640 HELPCTX(features_altscreen),
1641 conf_checkbox_handler, I(CONF_no_alt_screen));
1642 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1643 HELPCTX(features_retitle),
1644 conf_checkbox_handler,
1645 I(CONF_no_remote_wintitle));
1646 ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1647 HELPCTX(features_qtitle),
1648 conf_radiobutton_handler,
1649 I(CONF_remote_qtitle_action),
1650 "None", I(TITLE_NONE),
1651 "Empty string", I(TITLE_EMPTY),
1652 "Window title", I(TITLE_REAL), NULL);
1653 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1654 HELPCTX(features_dbackspace),
1655 conf_checkbox_handler, I(CONF_no_dbackspace));
1656 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1657 'r', HELPCTX(features_charset), conf_checkbox_handler,
1658 I(CONF_no_remote_charset));
1659 ctrl_checkbox(s, "Disable Arabic text shaping",
1660 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1661 I(CONF_arabicshaping));
1662 ctrl_checkbox(s, "Disable bidirectional text display",
1663 'd', HELPCTX(features_bidi), conf_checkbox_handler,
1669 str = dupprintf("Options controlling %s's window", appname);
1670 ctrl_settitle(b, "Window", str);
1673 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1674 ctrl_columns(s, 2, 50, 50);
1675 c = ctrl_editbox(s, "Columns", 'm', 100,
1676 HELPCTX(window_size),
1677 conf_editbox_handler, I(CONF_width), I(-1));
1678 c->generic.column = 0;
1679 c = ctrl_editbox(s, "Rows", 'r', 100,
1680 HELPCTX(window_size),
1681 conf_editbox_handler, I(CONF_height),I(-1));
1682 c->generic.column = 1;
1683 ctrl_columns(s, 1, 100);
1685 s = ctrl_getset(b, "Window", "scrollback",
1686 "Control the scrollback in the window");
1687 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1688 HELPCTX(window_scrollback),
1689 conf_editbox_handler, I(CONF_savelines), I(-1));
1690 ctrl_checkbox(s, "Display scrollbar", 'd',
1691 HELPCTX(window_scrollback),
1692 conf_checkbox_handler, I(CONF_scrollbar));
1693 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1694 HELPCTX(window_scrollback),
1695 conf_checkbox_handler, I(CONF_scroll_on_key));
1696 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1697 HELPCTX(window_scrollback),
1698 conf_checkbox_handler, I(CONF_scroll_on_disp));
1699 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1700 HELPCTX(window_erased),
1701 conf_checkbox_handler,
1702 I(CONF_erase_to_scrollback));
1705 * The Window/Appearance panel.
1707 str = dupprintf("Configure the appearance of %s's window", appname);
1708 ctrl_settitle(b, "Window/Appearance", str);
1711 s = ctrl_getset(b, "Window/Appearance", "cursor",
1712 "Adjust the use of the cursor");
1713 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1714 HELPCTX(appearance_cursor),
1715 conf_radiobutton_handler,
1716 I(CONF_cursor_type),
1718 "Underline", 'u', I(1),
1719 "Vertical line", 'v', I(2), NULL);
1720 ctrl_checkbox(s, "Cursor blinks", 'b',
1721 HELPCTX(appearance_cursor),
1722 conf_checkbox_handler, I(CONF_blink_cur));
1724 s = ctrl_getset(b, "Window/Appearance", "font",
1726 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1727 HELPCTX(appearance_font),
1728 conf_fontsel_handler, I(CONF_font));
1730 s = ctrl_getset(b, "Window/Appearance", "mouse",
1731 "Adjust the use of the mouse pointer");
1732 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1733 HELPCTX(appearance_hidemouse),
1734 conf_checkbox_handler, I(CONF_hide_mouseptr));
1736 s = ctrl_getset(b, "Window/Appearance", "border",
1737 "Adjust the window border");
1738 ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1739 HELPCTX(appearance_border),
1740 conf_editbox_handler,
1741 I(CONF_window_border), I(-1));
1744 * The Window/Behaviour panel.
1746 str = dupprintf("Configure the behaviour of %s's window", appname);
1747 ctrl_settitle(b, "Window/Behaviour", str);
1750 s = ctrl_getset(b, "Window/Behaviour", "title",
1751 "Adjust the behaviour of the window title");
1752 ctrl_editbox(s, "Window title:", 't', 100,
1753 HELPCTX(appearance_title),
1754 conf_editbox_handler, I(CONF_wintitle), I(1));
1755 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1756 HELPCTX(appearance_title),
1757 conf_checkbox_handler,
1758 I(CHECKBOX_INVERT | CONF_win_name_always));
1760 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1761 ctrl_checkbox(s, "Warn before closing window", 'w',
1762 HELPCTX(behaviour_closewarn),
1763 conf_checkbox_handler, I(CONF_warn_on_close));
1766 * The Window/Translation panel.
1768 ctrl_settitle(b, "Window/Translation",
1769 "Options controlling character set translation");
1771 s = ctrl_getset(b, "Window/Translation", "trans",
1772 "Character set translation");
1773 ctrl_combobox(s, "Remote character set:",
1774 'r', 100, HELPCTX(translation_codepage),
1775 codepage_handler, P(NULL), P(NULL));
1777 s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1778 ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1779 HELPCTX(translation_cjk_ambig_wide),
1780 conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1782 str = dupprintf("Adjust how %s handles line drawing characters", appname);
1783 s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1785 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1786 HELPCTX(translation_linedraw),
1787 conf_radiobutton_handler,
1789 "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1790 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1792 ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1793 HELPCTX(selection_linedraw),
1794 conf_checkbox_handler, I(CONF_rawcnp));
1797 * The Window/Selection panel.
1799 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1801 s = ctrl_getset(b, "Window/Selection", "mouse",
1802 "Control use of mouse");
1803 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1804 HELPCTX(selection_shiftdrag),
1805 conf_checkbox_handler, I(CONF_mouse_override));
1806 ctrl_radiobuttons(s,
1807 "Default selection mode (Alt+drag does the other one):",
1809 HELPCTX(selection_rect),
1810 conf_radiobutton_handler,
1811 I(CONF_rect_select),
1812 "Normal", 'n', I(0),
1813 "Rectangular block", 'r', I(1), NULL);
1815 s = ctrl_getset(b, "Window/Selection", "charclass",
1816 "Control the select-one-word-at-a-time mode");
1817 ccd = (struct charclass_data *)
1818 ctrl_alloc(b, sizeof(struct charclass_data));
1819 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1820 HELPCTX(selection_charclasses),
1821 charclass_handler, P(ccd));
1822 ccd->listbox->listbox.multisel = 1;
1823 ccd->listbox->listbox.ncols = 4;
1824 ccd->listbox->listbox.percentages = snewn(4, int);
1825 ccd->listbox->listbox.percentages[0] = 15;
1826 ccd->listbox->listbox.percentages[1] = 25;
1827 ccd->listbox->listbox.percentages[2] = 20;
1828 ccd->listbox->listbox.percentages[3] = 40;
1829 ctrl_columns(s, 2, 67, 33);
1830 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1831 HELPCTX(selection_charclasses),
1832 charclass_handler, P(ccd), P(NULL));
1833 ccd->editbox->generic.column = 0;
1834 ccd->button = ctrl_pushbutton(s, "Set", 's',
1835 HELPCTX(selection_charclasses),
1836 charclass_handler, P(ccd));
1837 ccd->button->generic.column = 1;
1838 ctrl_columns(s, 1, 100);
1841 * The Window/Colours panel.
1843 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1845 s = ctrl_getset(b, "Window/Colours", "general",
1846 "General options for colour usage");
1847 ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1848 HELPCTX(colours_ansi),
1849 conf_checkbox_handler, I(CONF_ansi_colour));
1850 ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1851 HELPCTX(colours_xterm256), conf_checkbox_handler,
1852 I(CONF_xterm_256_colour));
1853 ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
1854 HELPCTX(colours_bold),
1855 conf_radiobutton_handler, I(CONF_bold_style),
1861 str = dupprintf("Adjust the precise colours %s displays", appname);
1862 s = ctrl_getset(b, "Window/Colours", "adjust", str);
1864 ctrl_text(s, "Select a colour from the list, and then click the"
1865 " Modify button to change its appearance.",
1866 HELPCTX(colours_config));
1867 ctrl_columns(s, 2, 67, 33);
1868 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1869 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1870 HELPCTX(colours_config), colour_handler, P(cd));
1871 cd->listbox->generic.column = 0;
1872 cd->listbox->listbox.height = 7;
1873 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1874 c->generic.column = 1;
1875 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1876 colour_handler, P(cd), P(NULL));
1877 cd->redit->generic.column = 1;
1878 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1879 colour_handler, P(cd), P(NULL));
1880 cd->gedit->generic.column = 1;
1881 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1882 colour_handler, P(cd), P(NULL));
1883 cd->bedit->generic.column = 1;
1884 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1885 colour_handler, P(cd));
1886 cd->button->generic.column = 1;
1887 ctrl_columns(s, 1, 100);
1890 * The Connection panel. This doesn't show up if we're in a
1891 * non-network utility such as pterm. We tell this by being
1892 * passed a protocol < 0.
1894 if (protocol >= 0) {
1895 ctrl_settitle(b, "Connection", "Options controlling the connection");
1897 s = ctrl_getset(b, "Connection", "keepalive",
1898 "Sending of null packets to keep session active");
1899 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1900 HELPCTX(connection_keepalive),
1901 conf_editbox_handler, I(CONF_ping_interval),
1905 s = ctrl_getset(b, "Connection", "tcp",
1906 "Low-level TCP connection options");
1907 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1908 'n', HELPCTX(connection_nodelay),
1909 conf_checkbox_handler,
1910 I(CONF_tcp_nodelay));
1911 ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1912 'p', HELPCTX(connection_tcpkeepalive),
1913 conf_checkbox_handler,
1914 I(CONF_tcp_keepalives));
1916 s = ctrl_getset(b, "Connection", "ipversion",
1917 "Internet protocol version");
1918 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1919 HELPCTX(connection_ipversion),
1920 conf_radiobutton_handler,
1921 I(CONF_addressfamily),
1922 "Auto", 'u', I(ADDRTYPE_UNSPEC),
1923 "IPv4", '4', I(ADDRTYPE_IPV4),
1924 "IPv6", '6', I(ADDRTYPE_IPV6),
1929 char *label = backend_from_proto(PROT_SSH) ?
1930 "Logical name of remote host (e.g. for SSH key lookup):" :
1931 "Logical name of remote host:";
1932 s = ctrl_getset(b, "Connection", "identity",
1933 "Logical name of remote host");
1934 ctrl_editbox(s, label, 'm', 100,
1935 HELPCTX(connection_loghost),
1936 conf_editbox_handler, I(CONF_loghost), I(1));
1941 * A sub-panel Connection/Data, containing options that
1942 * decide on data to send to the server.
1945 ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1947 s = ctrl_getset(b, "Connection/Data", "login",
1949 ctrl_editbox(s, "Auto-login username", 'u', 50,
1950 HELPCTX(connection_username),
1951 conf_editbox_handler, I(CONF_username), I(1));
1953 /* We assume the local username is sufficiently stable
1954 * to include on the dialog box. */
1955 char *user = get_username();
1956 char *userlabel = dupprintf("Use system username (%s)",
1959 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1960 HELPCTX(connection_username_from_env),
1961 conf_radiobutton_handler,
1962 I(CONF_username_from_env),
1969 s = ctrl_getset(b, "Connection/Data", "term",
1970 "Terminal details");
1971 ctrl_editbox(s, "Terminal-type string", 't', 50,
1972 HELPCTX(connection_termtype),
1973 conf_editbox_handler, I(CONF_termtype), I(1));
1974 ctrl_editbox(s, "Terminal speeds", 's', 50,
1975 HELPCTX(connection_termspeed),
1976 conf_editbox_handler, I(CONF_termspeed), I(1));
1978 s = ctrl_getset(b, "Connection/Data", "env",
1979 "Environment variables");
1980 ctrl_columns(s, 2, 80, 20);
1981 ed = (struct environ_data *)
1982 ctrl_alloc(b, sizeof(struct environ_data));
1983 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1984 HELPCTX(telnet_environ),
1985 environ_handler, P(ed), P(NULL));
1986 ed->varbox->generic.column = 0;
1987 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1988 HELPCTX(telnet_environ),
1989 environ_handler, P(ed), P(NULL));
1990 ed->valbox->generic.column = 0;
1991 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1992 HELPCTX(telnet_environ),
1993 environ_handler, P(ed));
1994 ed->addbutton->generic.column = 1;
1995 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1996 HELPCTX(telnet_environ),
1997 environ_handler, P(ed));
1998 ed->rembutton->generic.column = 1;
1999 ctrl_columns(s, 1, 100);
2000 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2001 HELPCTX(telnet_environ),
2002 environ_handler, P(ed));
2003 ed->listbox->listbox.height = 3;
2004 ed->listbox->listbox.ncols = 2;
2005 ed->listbox->listbox.percentages = snewn(2, int);
2006 ed->listbox->listbox.percentages[0] = 30;
2007 ed->listbox->listbox.percentages[1] = 70;
2014 * The Connection/Proxy panel.
2016 ctrl_settitle(b, "Connection/Proxy",
2017 "Options controlling proxy usage");
2019 s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
2020 ctrl_radiobuttons(s, "Proxy type:", 't', 3,
2021 HELPCTX(proxy_type),
2022 conf_radiobutton_handler,
2024 "None", I(PROXY_NONE),
2025 "SOCKS 4", I(PROXY_SOCKS4),
2026 "SOCKS 5", I(PROXY_SOCKS5),
2027 "HTTP", I(PROXY_HTTP),
2028 "Telnet", I(PROXY_TELNET),
2030 ctrl_columns(s, 2, 80, 20);
2031 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
2032 HELPCTX(proxy_main),
2033 conf_editbox_handler,
2034 I(CONF_proxy_host), I(1));
2035 c->generic.column = 0;
2036 c = ctrl_editbox(s, "Port", 'p', 100,
2037 HELPCTX(proxy_main),
2038 conf_editbox_handler,
2041 c->generic.column = 1;
2042 ctrl_columns(s, 1, 100);
2043 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
2044 HELPCTX(proxy_exclude),
2045 conf_editbox_handler,
2046 I(CONF_proxy_exclude_list), I(1));
2047 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
2048 HELPCTX(proxy_exclude),
2049 conf_checkbox_handler,
2050 I(CONF_even_proxy_localhost));
2051 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
2053 conf_radiobutton_handler,
2057 "Yes", I(FORCE_ON), NULL);
2058 ctrl_editbox(s, "Username", 'u', 60,
2059 HELPCTX(proxy_auth),
2060 conf_editbox_handler,
2061 I(CONF_proxy_username), I(1));
2062 c = ctrl_editbox(s, "Password", 'w', 60,
2063 HELPCTX(proxy_auth),
2064 conf_editbox_handler,
2065 I(CONF_proxy_password), I(1));
2066 c->editbox.password = 1;
2067 ctrl_editbox(s, "Telnet command", 'm', 100,
2068 HELPCTX(proxy_command),
2069 conf_editbox_handler,
2070 I(CONF_proxy_telnet_command), I(1));
2074 * The Telnet panel exists in the base config box, and in a
2075 * mid-session reconfig box _if_ we're using Telnet.
2077 if (!midsession || protocol == PROT_TELNET) {
2079 * The Connection/Telnet panel.
2081 ctrl_settitle(b, "Connection/Telnet",
2082 "Options controlling Telnet connections");
2084 s = ctrl_getset(b, "Connection/Telnet", "protocol",
2085 "Telnet protocol adjustments");
2088 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2090 HELPCTX(telnet_oldenviron),
2091 conf_radiobutton_handler,
2092 I(CONF_rfc_environ),
2093 "BSD (commonplace)", 'b', I(0),
2094 "RFC 1408 (unusual)", 'f', I(1), NULL);
2095 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2096 HELPCTX(telnet_passive),
2097 conf_radiobutton_handler,
2098 I(CONF_passive_telnet),
2099 "Passive", I(1), "Active", I(0), NULL);
2101 ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2102 HELPCTX(telnet_specialkeys),
2103 conf_checkbox_handler,
2104 I(CONF_telnet_keyboard));
2105 ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2106 'm', HELPCTX(telnet_newline),
2107 conf_checkbox_handler,
2108 I(CONF_telnet_newline));
2114 * The Connection/Rlogin panel.
2116 ctrl_settitle(b, "Connection/Rlogin",
2117 "Options controlling Rlogin connections");
2119 s = ctrl_getset(b, "Connection/Rlogin", "data",
2120 "Data to send to the server");
2121 ctrl_editbox(s, "Local username:", 'l', 50,
2122 HELPCTX(rlogin_localuser),
2123 conf_editbox_handler, I(CONF_localusername), I(1));
2128 * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2129 * when we're not doing SSH.
2132 if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2135 * The Connection/SSH panel.
2137 ctrl_settitle(b, "Connection/SSH",
2138 "Options controlling SSH connections");
2140 /* SSH-1 or connection-sharing downstream */
2141 if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
2142 s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2143 ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2144 "session; it is only here so that sub-panels of it can "
2145 "exist without looking strange.", HELPCTX(no_help));
2150 s = ctrl_getset(b, "Connection/SSH", "data",
2151 "Data to send to the server");
2152 ctrl_editbox(s, "Remote command:", 'r', 100,
2153 HELPCTX(ssh_command),
2154 conf_editbox_handler, I(CONF_remote_cmd), I(1));
2156 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2157 ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2158 HELPCTX(ssh_noshell),
2159 conf_checkbox_handler,
2160 I(CONF_ssh_no_shell));
2163 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2164 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2166 ctrl_checkbox(s, "Enable compression", 'e',
2167 HELPCTX(ssh_compress),
2168 conf_checkbox_handler,
2169 I(CONF_compression));
2173 s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
2175 ctrl_checkbox(s, "Share SSH connections if possible", 's',
2177 conf_checkbox_handler,
2178 I(CONF_ssh_connection_sharing));
2180 ctrl_text(s, "Permitted roles in a shared connection:",
2181 HELPCTX(ssh_share));
2182 ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
2184 conf_checkbox_handler,
2185 I(CONF_ssh_connection_sharing_upstream));
2186 ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
2188 conf_checkbox_handler,
2189 I(CONF_ssh_connection_sharing_downstream));
2193 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2195 ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2196 HELPCTX(ssh_protocol),
2197 conf_radiobutton_handler,
2199 "1 only", 'l', I(0),
2202 "2 only", 'y', I(3), NULL);
2206 * The Connection/SSH/Kex panel. (Owing to repeat key
2207 * exchange, much of this is meaningful in mid-session _if_
2208 * we're using SSH-2 and are not a connection-sharing
2209 * downstream, or haven't decided yet.)
2211 if (protcfginfo != 1 && protcfginfo != -1) {
2212 ctrl_settitle(b, "Connection/SSH/Kex",
2213 "Options controlling SSH key exchange");
2215 s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2216 "Key exchange algorithm options");
2217 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2218 HELPCTX(ssh_kexlist),
2219 kexlist_handler, P(NULL));
2220 c->listbox.height = 5;
2222 s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2223 "Options controlling key re-exchange");
2225 ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2226 HELPCTX(ssh_kex_repeat),
2227 conf_editbox_handler,
2228 I(CONF_ssh_rekey_time),
2230 ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2231 HELPCTX(ssh_kex_repeat),
2232 conf_editbox_handler,
2233 I(CONF_ssh_rekey_data),
2235 ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2236 HELPCTX(ssh_kex_repeat));
2240 * Manual host key configuration is irrelevant mid-session,
2241 * as we enforce that the host key for rekeys is the
2242 * same as that used at the start of the session.
2245 s = ctrl_getset(b, "Connection/SSH/Kex", "hostkeys",
2246 "Manually configure host keys for this connection");
2248 ctrl_columns(s, 2, 75, 25);
2249 c = ctrl_text(s, "Host keys or fingerprints to accept:",
2250 HELPCTX(ssh_kex_manual_hostkeys));
2251 c->generic.column = 0;
2252 /* You want to select from the list, _then_ hit Remove. So
2253 * tab order should be that way round. */
2254 mh = (struct manual_hostkey_data *)
2255 ctrl_alloc(b,sizeof(struct manual_hostkey_data));
2256 mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2257 HELPCTX(ssh_kex_manual_hostkeys),
2258 manual_hostkey_handler, P(mh));
2259 mh->rembutton->generic.column = 1;
2260 mh->rembutton->generic.tabdelay = 1;
2261 mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2262 HELPCTX(ssh_kex_manual_hostkeys),
2263 manual_hostkey_handler, P(mh));
2264 /* This list box can't be very tall, because there's not
2265 * much room in the pane on Windows at least. This makes
2266 * it become really unhelpful if a horizontal scrollbar
2267 * appears, so we suppress that. */
2268 mh->listbox->listbox.height = 2;
2269 mh->listbox->listbox.hscroll = FALSE;
2270 ctrl_tabdelay(s, mh->rembutton);
2271 mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
2272 HELPCTX(ssh_kex_manual_hostkeys),
2273 manual_hostkey_handler, P(mh), P(NULL));
2274 mh->keybox->generic.column = 0;
2275 mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
2276 HELPCTX(ssh_kex_manual_hostkeys),
2277 manual_hostkey_handler, P(mh));
2278 mh->addbutton->generic.column = 1;
2279 ctrl_columns(s, 1, 100);
2282 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2284 * The Connection/SSH/Cipher panel.
2286 ctrl_settitle(b, "Connection/SSH/Cipher",
2287 "Options controlling SSH encryption");
2289 s = ctrl_getset(b, "Connection/SSH/Cipher",
2290 "encryption", "Encryption options");
2291 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2292 HELPCTX(ssh_ciphers),
2293 cipherlist_handler, P(NULL));
2294 c->listbox.height = 6;
2296 ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2297 HELPCTX(ssh_ciphers),
2298 conf_checkbox_handler,
2299 I(CONF_ssh2_des_cbc));
2305 * The Connection/SSH/Auth panel.
2307 ctrl_settitle(b, "Connection/SSH/Auth",
2308 "Options controlling SSH authentication");
2310 s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2311 ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2312 HELPCTX(ssh_auth_bypass),
2313 conf_checkbox_handler,
2314 I(CONF_ssh_no_userauth));
2315 ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2316 'd', HELPCTX(ssh_auth_banner),
2317 conf_checkbox_handler,
2318 I(CONF_ssh_show_banner));
2320 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2321 "Authentication methods");
2322 ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2323 HELPCTX(ssh_auth_pageant),
2324 conf_checkbox_handler,
2326 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2327 HELPCTX(ssh_auth_tis),
2328 conf_checkbox_handler,
2329 I(CONF_try_tis_auth));
2330 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2331 'i', HELPCTX(ssh_auth_ki),
2332 conf_checkbox_handler,
2333 I(CONF_try_ki_auth));
2335 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2336 "Authentication parameters");
2337 ctrl_checkbox(s, "Allow agent forwarding", 'f',
2338 HELPCTX(ssh_auth_agentfwd),
2339 conf_checkbox_handler, I(CONF_agentfwd));
2340 ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2341 HELPCTX(ssh_auth_changeuser),
2342 conf_checkbox_handler,
2343 I(CONF_change_username));
2344 ctrl_filesel(s, "Private key file for authentication:", 'k',
2345 FILTER_KEY_FILES, FALSE, "Select private key file",
2346 HELPCTX(ssh_auth_privkey),
2347 conf_filesel_handler, I(CONF_keyfile));
2351 * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2352 * the main Auth panel.
2354 ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2355 "Options controlling GSSAPI authentication");
2356 s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2358 ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2359 't', HELPCTX(ssh_gssapi),
2360 conf_checkbox_handler,
2361 I(CONF_try_gssapi_auth));
2363 ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2364 HELPCTX(ssh_gssapi_delegation),
2365 conf_checkbox_handler,
2369 * GSSAPI library selection.
2372 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2373 'p', HELPCTX(ssh_gssapi_libraries),
2374 gsslist_handler, P(NULL));
2375 c->listbox.height = ngsslibs;
2378 * I currently assume that if more than one GSS
2379 * library option is available, then one of them is
2380 * 'user-supplied' and so we should present the
2381 * following file selector. This is at least half-
2382 * reasonable, because if we're using statically
2383 * linked GSSAPI then there will only be one option
2384 * and no way to load from a user-supplied library,
2385 * whereas if we're using dynamic libraries then
2386 * there will almost certainly be some default
2387 * option in addition to a user-supplied path. If
2388 * anyone ever ports PuTTY to a system on which
2389 * dynamic-library GSSAPI is available but there is
2390 * absolutely no consensus on where to keep the
2391 * libraries, there'll need to be a flag alongside
2392 * ngsslibs to control whether the file selector is
2396 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2397 FILTER_DYNLIB_FILES, FALSE, "Select library file",
2398 HELPCTX(ssh_gssapi_libraries),
2399 conf_filesel_handler,
2400 I(CONF_ssh_gss_custom));
2407 * The Connection/SSH/TTY panel.
2409 ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2411 s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2412 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2414 conf_checkbox_handler,
2417 s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2419 td = (struct ttymodes_data *)
2420 ctrl_alloc(b, sizeof(struct ttymodes_data));
2421 ctrl_columns(s, 2, 75, 25);
2422 c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2423 c->generic.column = 0;
2424 td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2425 HELPCTX(ssh_ttymodes),
2426 ttymodes_handler, P(td));
2427 td->rembutton->generic.column = 1;
2428 td->rembutton->generic.tabdelay = 1;
2429 ctrl_columns(s, 1, 100);
2430 td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2431 HELPCTX(ssh_ttymodes),
2432 ttymodes_handler, P(td));
2433 td->listbox->listbox.multisel = 1;
2434 td->listbox->listbox.height = 4;
2435 td->listbox->listbox.ncols = 2;
2436 td->listbox->listbox.percentages = snewn(2, int);
2437 td->listbox->listbox.percentages[0] = 40;
2438 td->listbox->listbox.percentages[1] = 60;
2439 ctrl_tabdelay(s, td->rembutton);
2440 ctrl_columns(s, 2, 75, 25);
2441 td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2442 HELPCTX(ssh_ttymodes),
2443 ttymodes_handler, P(td));
2444 td->modelist->generic.column = 0;
2445 td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2446 HELPCTX(ssh_ttymodes),
2447 ttymodes_handler, P(td));
2448 td->addbutton->generic.column = 1;
2449 td->addbutton->generic.tabdelay = 1;
2450 ctrl_columns(s, 1, 100); /* column break */
2451 /* Bit of a hack to get the value radio buttons and
2452 * edit-box on the same row. */
2453 ctrl_columns(s, 3, 25, 50, 25);
2454 c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2455 c->generic.column = 0;
2456 td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2457 HELPCTX(ssh_ttymodes),
2458 ttymodes_handler, P(td),
2459 "Auto", NO_SHORTCUT, P(NULL),
2460 "This:", NO_SHORTCUT, P(NULL),
2462 td->valradio->generic.column = 1;
2463 td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2464 HELPCTX(ssh_ttymodes),
2465 ttymodes_handler, P(td), P(NULL));
2466 td->valbox->generic.column = 2;
2467 ctrl_tabdelay(s, td->addbutton);
2473 * The Connection/SSH/X11 panel.
2475 ctrl_settitle(b, "Connection/SSH/X11",
2476 "Options controlling SSH X11 forwarding");
2478 s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2479 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2480 HELPCTX(ssh_tunnels_x11),
2481 conf_checkbox_handler,I(CONF_x11_forward));
2482 ctrl_editbox(s, "X display location", 'x', 50,
2483 HELPCTX(ssh_tunnels_x11),
2484 conf_editbox_handler, I(CONF_x11_display), I(1));
2485 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2486 HELPCTX(ssh_tunnels_x11auth),
2487 conf_radiobutton_handler,
2489 "MIT-Magic-Cookie-1", I(X11_MIT),
2490 "XDM-Authorization-1", I(X11_XDM), NULL);
2494 * The Tunnels panel _is_ still available in mid-session.
2496 ctrl_settitle(b, "Connection/SSH/Tunnels",
2497 "Options controlling SSH port forwarding");
2499 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2501 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2502 HELPCTX(ssh_tunnels_portfwd_localhost),
2503 conf_checkbox_handler,
2504 I(CONF_lport_acceptall));
2505 ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2506 HELPCTX(ssh_tunnels_portfwd_localhost),
2507 conf_checkbox_handler,
2508 I(CONF_rport_acceptall));
2510 ctrl_columns(s, 3, 55, 20, 25);
2511 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2512 c->generic.column = COLUMN_FIELD(0,2);
2513 /* You want to select from the list, _then_ hit Remove. So tab order
2514 * should be that way round. */
2515 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2516 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2517 HELPCTX(ssh_tunnels_portfwd),
2518 portfwd_handler, P(pfd));
2519 pfd->rembutton->generic.column = 2;
2520 pfd->rembutton->generic.tabdelay = 1;
2521 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2522 HELPCTX(ssh_tunnels_portfwd),
2523 portfwd_handler, P(pfd));
2524 pfd->listbox->listbox.height = 3;
2525 pfd->listbox->listbox.ncols = 2;
2526 pfd->listbox->listbox.percentages = snewn(2, int);
2527 pfd->listbox->listbox.percentages[0] = 20;
2528 pfd->listbox->listbox.percentages[1] = 80;
2529 ctrl_tabdelay(s, pfd->rembutton);
2530 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2531 /* You want to enter source, destination and type, _then_ hit Add.
2532 * Again, we adjust the tab order to reflect this. */
2533 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2534 HELPCTX(ssh_tunnels_portfwd),
2535 portfwd_handler, P(pfd));
2536 pfd->addbutton->generic.column = 2;
2537 pfd->addbutton->generic.tabdelay = 1;
2538 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2539 HELPCTX(ssh_tunnels_portfwd),
2540 portfwd_handler, P(pfd), P(NULL));
2541 pfd->sourcebox->generic.column = 0;
2542 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2543 HELPCTX(ssh_tunnels_portfwd),
2544 portfwd_handler, P(pfd), P(NULL));
2545 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2546 HELPCTX(ssh_tunnels_portfwd),
2547 portfwd_handler, P(pfd),
2548 "Local", 'l', P(NULL),
2549 "Remote", 'm', P(NULL),
2550 "Dynamic", 'y', P(NULL),
2553 pfd->addressfamily =
2554 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2555 HELPCTX(ssh_tunnels_portfwd_ipversion),
2556 portfwd_handler, P(pfd),
2557 "Auto", 'u', I(ADDRTYPE_UNSPEC),
2558 "IPv4", '4', I(ADDRTYPE_IPV4),
2559 "IPv6", '6', I(ADDRTYPE_IPV6),
2562 ctrl_tabdelay(s, pfd->addbutton);
2563 ctrl_columns(s, 1, 100);
2567 * The Connection/SSH/Bugs panel.
2569 ctrl_settitle(b, "Connection/SSH/Bugs",
2570 "Workarounds for SSH server bugs");
2572 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2573 "Detection of known bugs in SSH servers");
2574 ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2575 HELPCTX(ssh_bugs_ignore1),
2576 sshbug_handler, I(CONF_sshbug_ignore1));
2577 ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2578 HELPCTX(ssh_bugs_plainpw1),
2579 sshbug_handler, I(CONF_sshbug_plainpw1));
2580 ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2581 HELPCTX(ssh_bugs_rsa1),
2582 sshbug_handler, I(CONF_sshbug_rsa1));
2583 ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2584 HELPCTX(ssh_bugs_ignore2),
2585 sshbug_handler, I(CONF_sshbug_ignore2));
2586 ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
2587 20, HELPCTX(ssh_bugs_winadj),
2588 sshbug_handler, I(CONF_sshbug_winadj));
2589 ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2590 HELPCTX(ssh_bugs_hmac2),
2591 sshbug_handler, I(CONF_sshbug_hmac2));
2592 ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2593 HELPCTX(ssh_bugs_derivekey2),
2594 sshbug_handler, I(CONF_sshbug_derivekey2));
2595 ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2596 HELPCTX(ssh_bugs_rsapad2),
2597 sshbug_handler, I(CONF_sshbug_rsapad2));
2598 ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2599 HELPCTX(ssh_bugs_pksessid2),
2600 sshbug_handler, I(CONF_sshbug_pksessid2));
2601 ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2602 HELPCTX(ssh_bugs_rekey2),
2603 sshbug_handler, I(CONF_sshbug_rekey2));
2604 ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2605 HELPCTX(ssh_bugs_maxpkt2),
2606 sshbug_handler, I(CONF_sshbug_maxpkt2));
2607 ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
2608 HELPCTX(ssh_bugs_chanreq),
2609 sshbug_handler, I(CONF_sshbug_chanreq));