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 { "-- warn below here --", KEX_WARN }
439 /* Set up the "kex preference" box. */
440 /* (kexlist assumed to contain all algorithms) */
441 dlg_update_start(ctrl, dlg);
442 dlg_listbox_clear(ctrl, dlg);
443 for (i = 0; i < KEX_MAX; i++) {
444 int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
447 for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
448 if (kexes[j].k == k) {
453 dlg_listbox_addwithid(ctrl, dlg, kstr, k);
455 dlg_update_done(ctrl, dlg);
457 } else if (event == EVENT_VALCHANGE) {
460 /* Update array to match the list box. */
461 for (i=0; i < KEX_MAX; i++)
462 conf_set_int_int(conf, CONF_ssh_kexlist, i,
463 dlg_listbox_getid(ctrl, dlg, i));
467 static void printerbox_handler(union control *ctrl, void *dlg,
468 void *data, int event)
470 Conf *conf = (Conf *)data;
471 if (event == EVENT_REFRESH) {
476 dlg_update_start(ctrl, dlg);
478 * Some backends may wish to disable the drop-down list on
479 * this edit box. Be prepared for this.
481 if (ctrl->editbox.has_list) {
482 dlg_listbox_clear(ctrl, dlg);
483 dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
484 pe = printer_start_enum(&nprinters);
485 for (i = 0; i < nprinters; i++)
486 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
487 printer_finish_enum(pe);
489 printer = conf_get_str(conf, CONF_printer);
491 printer = PRINTER_DISABLED_STRING;
492 dlg_editbox_set(ctrl, dlg, printer);
493 dlg_update_done(ctrl, dlg);
494 } else if (event == EVENT_VALCHANGE) {
495 char *printer = dlg_editbox_get(ctrl, dlg);
496 if (!strcmp(printer, PRINTER_DISABLED_STRING))
498 conf_set_str(conf, CONF_printer, printer);
503 static void codepage_handler(union control *ctrl, void *dlg,
504 void *data, int event)
506 Conf *conf = (Conf *)data;
507 if (event == EVENT_REFRESH) {
509 const char *cp, *thiscp;
510 dlg_update_start(ctrl, dlg);
511 thiscp = cp_name(decode_codepage(conf_get_str(conf,
512 CONF_line_codepage)));
513 dlg_listbox_clear(ctrl, dlg);
514 for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
515 dlg_listbox_add(ctrl, dlg, cp);
516 dlg_editbox_set(ctrl, dlg, thiscp);
517 conf_set_str(conf, CONF_line_codepage, thiscp);
518 dlg_update_done(ctrl, dlg);
519 } else if (event == EVENT_VALCHANGE) {
520 char *codepage = dlg_editbox_get(ctrl, dlg);
521 conf_set_str(conf, CONF_line_codepage,
522 cp_name(decode_codepage(codepage)));
527 static void sshbug_handler(union control *ctrl, void *dlg,
528 void *data, int event)
530 Conf *conf = (Conf *)data;
531 if (event == EVENT_REFRESH) {
533 * We must fetch the previously configured value from the Conf
534 * before we start modifying the drop-down list, otherwise the
535 * spurious SELCHANGE we trigger in the process will overwrite
536 * the value we wanted to keep.
538 int oldconf = conf_get_int(conf, ctrl->listbox.context.i);
539 dlg_update_start(ctrl, dlg);
540 dlg_listbox_clear(ctrl, dlg);
541 dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
542 dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
543 dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
545 case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
546 case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
547 case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
549 dlg_update_done(ctrl, dlg);
550 } else if (event == EVENT_SELCHANGE) {
551 int i = dlg_listbox_index(ctrl, dlg);
555 i = dlg_listbox_getid(ctrl, dlg, i);
556 conf_set_int(conf, ctrl->listbox.context.i, i);
560 struct sessionsaver_data {
561 union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
562 union control *okbutton, *cancelbutton;
563 struct sesslist sesslist;
565 char *savedsession; /* the current contents of ssd->editbox */
568 static void sessionsaver_data_free(void *ssdv)
570 struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
571 sfree(ssd->savedsession);
576 * Helper function to load the session selected in the list box, if
577 * any, as this is done in more than one place below. Returns 0 for
580 static int load_selected_session(struct sessionsaver_data *ssd,
581 void *dlg, Conf *conf, int *maybe_launch)
583 int i = dlg_listbox_index(ssd->listbox, dlg);
589 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
590 load_settings(ssd->sesslist.sessions[i], conf);
591 sfree(ssd->savedsession);
592 ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]);
594 *maybe_launch = !isdef;
595 dlg_refresh(NULL, dlg);
596 /* Restore the selection, which might have been clobbered by
597 * changing the value of the edit box. */
598 dlg_listbox_select(ssd->listbox, dlg, i);
602 static void sessionsaver_handler(union control *ctrl, void *dlg,
603 void *data, int event)
605 Conf *conf = (Conf *)data;
606 struct sessionsaver_data *ssd =
607 (struct sessionsaver_data *)ctrl->generic.context.p;
609 if (event == EVENT_REFRESH) {
610 if (ctrl == ssd->editbox) {
611 dlg_editbox_set(ctrl, dlg, ssd->savedsession);
612 } else if (ctrl == ssd->listbox) {
614 dlg_update_start(ctrl, dlg);
615 dlg_listbox_clear(ctrl, dlg);
616 for (i = 0; i < ssd->sesslist.nsessions; i++)
617 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
618 dlg_update_done(ctrl, dlg);
620 } else if (event == EVENT_VALCHANGE) {
621 int top, bottom, halfway, i;
622 if (ctrl == ssd->editbox) {
623 sfree(ssd->savedsession);
624 ssd->savedsession = dlg_editbox_get(ctrl, dlg);
625 top = ssd->sesslist.nsessions;
627 while (top-bottom > 1) {
628 halfway = (top+bottom)/2;
629 i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]);
636 if (top == ssd->sesslist.nsessions) {
639 dlg_listbox_select(ssd->listbox, dlg, top);
641 } else if (event == EVENT_ACTION) {
643 if (!ssd->midsession &&
644 (ctrl == ssd->listbox ||
645 (ssd->loadbutton && ctrl == ssd->loadbutton))) {
647 * The user has double-clicked a session, or hit Load.
648 * We must load the selected session, and then
649 * terminate the configuration dialog _if_ there was a
650 * double-click on the list box _and_ that session
651 * contains a hostname.
653 if (load_selected_session(ssd, dlg, conf, &mbl) &&
654 (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
655 dlg_end(dlg, 1); /* it's all over, and succeeded */
657 } else if (ctrl == ssd->savebutton) {
658 int isdef = !strcmp(ssd->savedsession, "Default Settings");
659 if (!ssd->savedsession[0]) {
660 int i = dlg_listbox_index(ssd->listbox, dlg);
665 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
666 sfree(ssd->savedsession);
667 ssd->savedsession = dupstr(isdef ? "" :
668 ssd->sesslist.sessions[i]);
671 char *errmsg = save_settings(ssd->savedsession, conf);
673 dlg_error_msg(dlg, errmsg);
677 get_sesslist(&ssd->sesslist, FALSE);
678 get_sesslist(&ssd->sesslist, TRUE);
679 dlg_refresh(ssd->editbox, dlg);
680 dlg_refresh(ssd->listbox, dlg);
681 } else if (!ssd->midsession &&
682 ssd->delbutton && ctrl == ssd->delbutton) {
683 int i = dlg_listbox_index(ssd->listbox, dlg);
687 del_settings(ssd->sesslist.sessions[i]);
688 get_sesslist(&ssd->sesslist, FALSE);
689 get_sesslist(&ssd->sesslist, TRUE);
690 dlg_refresh(ssd->listbox, dlg);
692 } else if (ctrl == ssd->okbutton) {
693 if (ssd->midsession) {
694 /* In a mid-session Change Settings, Apply is always OK. */
699 * Annoying special case. If the `Open' button is
700 * pressed while no host name is currently set, _and_
701 * the session list previously had the focus, _and_
702 * there was a session selected in that which had a
703 * valid host name in it, then load it and go.
705 if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
706 !conf_launchable(conf)) {
707 Conf *conf2 = conf_new();
709 if (!load_selected_session(ssd, dlg, conf2, &mbl)) {
714 /* If at this point we have a valid session, go! */
715 if (mbl && conf_launchable(conf2)) {
716 conf_copy_into(conf, conf2);
726 * Otherwise, do the normal thing: if we have a valid
727 * session, get going.
729 if (conf_launchable(conf)) {
733 } else if (ctrl == ssd->cancelbutton) {
739 struct charclass_data {
740 union control *listbox, *editbox, *button;
743 static void charclass_handler(union control *ctrl, void *dlg,
744 void *data, int event)
746 Conf *conf = (Conf *)data;
747 struct charclass_data *ccd =
748 (struct charclass_data *)ctrl->generic.context.p;
750 if (event == EVENT_REFRESH) {
751 if (ctrl == ccd->listbox) {
753 dlg_update_start(ctrl, dlg);
754 dlg_listbox_clear(ctrl, dlg);
755 for (i = 0; i < 128; i++) {
757 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
758 (i >= 0x21 && i != 0x7F) ? i : ' ',
759 conf_get_int_int(conf, CONF_wordness, i));
760 dlg_listbox_add(ctrl, dlg, str);
762 dlg_update_done(ctrl, dlg);
764 } else if (event == EVENT_ACTION) {
765 if (ctrl == ccd->button) {
768 str = dlg_editbox_get(ccd->editbox, dlg);
771 for (i = 0; i < 128; i++) {
772 if (dlg_listbox_issel(ccd->listbox, dlg, i))
773 conf_set_int_int(conf, CONF_wordness, i, n);
775 dlg_refresh(ccd->listbox, dlg);
781 union control *listbox, *redit, *gedit, *bedit, *button;
784 static const char *const colours[] = {
785 "Default Foreground", "Default Bold Foreground",
786 "Default Background", "Default Bold Background",
787 "Cursor Text", "Cursor Colour",
788 "ANSI Black", "ANSI Black Bold",
789 "ANSI Red", "ANSI Red Bold",
790 "ANSI Green", "ANSI Green Bold",
791 "ANSI Yellow", "ANSI Yellow Bold",
792 "ANSI Blue", "ANSI Blue Bold",
793 "ANSI Magenta", "ANSI Magenta Bold",
794 "ANSI Cyan", "ANSI Cyan Bold",
795 "ANSI White", "ANSI White Bold"
798 static void colour_handler(union control *ctrl, void *dlg,
799 void *data, int event)
801 Conf *conf = (Conf *)data;
802 struct colour_data *cd =
803 (struct colour_data *)ctrl->generic.context.p;
804 int update = FALSE, clear = FALSE, r, g, b;
806 if (event == EVENT_REFRESH) {
807 if (ctrl == cd->listbox) {
809 dlg_update_start(ctrl, dlg);
810 dlg_listbox_clear(ctrl, dlg);
811 for (i = 0; i < lenof(colours); i++)
812 dlg_listbox_add(ctrl, dlg, colours[i]);
813 dlg_update_done(ctrl, dlg);
817 } else if (event == EVENT_SELCHANGE) {
818 if (ctrl == cd->listbox) {
819 /* The user has selected a colour. Update the RGB text. */
820 int i = dlg_listbox_index(ctrl, dlg);
825 r = conf_get_int_int(conf, CONF_colours, i*3+0);
826 g = conf_get_int_int(conf, CONF_colours, i*3+1);
827 b = conf_get_int_int(conf, CONF_colours, i*3+2);
831 } else if (event == EVENT_VALCHANGE) {
832 if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
833 /* The user has changed the colour using the edit boxes. */
837 str = dlg_editbox_get(ctrl, dlg);
840 if (cval > 255) cval = 255;
841 if (cval < 0) cval = 0;
843 i = dlg_listbox_index(cd->listbox, dlg);
845 if (ctrl == cd->redit)
846 conf_set_int_int(conf, CONF_colours, i*3+0, cval);
847 else if (ctrl == cd->gedit)
848 conf_set_int_int(conf, CONF_colours, i*3+1, cval);
849 else if (ctrl == cd->bedit)
850 conf_set_int_int(conf, CONF_colours, i*3+2, cval);
853 } else if (event == EVENT_ACTION) {
854 if (ctrl == cd->button) {
855 int i = dlg_listbox_index(cd->listbox, dlg);
861 * Start a colour selector, which will send us an
862 * EVENT_CALLBACK when it's finished and allow us to
863 * pick up the results.
865 dlg_coloursel_start(ctrl, dlg,
866 conf_get_int_int(conf, CONF_colours, i*3+0),
867 conf_get_int_int(conf, CONF_colours, i*3+1),
868 conf_get_int_int(conf, CONF_colours, i*3+2));
870 } else if (event == EVENT_CALLBACK) {
871 if (ctrl == cd->button) {
872 int i = dlg_listbox_index(cd->listbox, dlg);
874 * Collect the results of the colour selector. Will
875 * return nonzero on success, or zero if the colour
876 * selector did nothing (user hit Cancel, for example).
878 if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
879 conf_set_int_int(conf, CONF_colours, i*3+0, r);
880 conf_set_int_int(conf, CONF_colours, i*3+1, g);
881 conf_set_int_int(conf, CONF_colours, i*3+2, b);
890 dlg_editbox_set(cd->redit, dlg, "");
891 dlg_editbox_set(cd->gedit, dlg, "");
892 dlg_editbox_set(cd->bedit, dlg, "");
895 sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
896 sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
897 sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
902 struct ttymodes_data {
903 union control *modelist, *valradio, *valbox;
904 union control *addbutton, *rembutton, *listbox;
907 static void ttymodes_handler(union control *ctrl, void *dlg,
908 void *data, int event)
910 Conf *conf = (Conf *)data;
911 struct ttymodes_data *td =
912 (struct ttymodes_data *)ctrl->generic.context.p;
914 if (event == EVENT_REFRESH) {
915 if (ctrl == td->listbox) {
917 dlg_update_start(ctrl, dlg);
918 dlg_listbox_clear(ctrl, dlg);
919 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
921 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
922 char *disp = dupprintf("%s\t%s", key,
923 (val[0] == 'A') ? "(auto)" : val+1);
924 dlg_listbox_add(ctrl, dlg, disp);
927 dlg_update_done(ctrl, dlg);
928 } else if (ctrl == td->modelist) {
930 dlg_update_start(ctrl, dlg);
931 dlg_listbox_clear(ctrl, dlg);
932 for (i = 0; ttymodes[i]; i++)
933 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
934 dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
935 dlg_update_done(ctrl, dlg);
936 } else if (ctrl == td->valradio) {
937 dlg_radiobutton_set(ctrl, dlg, 0);
939 } else if (event == EVENT_ACTION) {
940 if (ctrl == td->addbutton) {
941 int ind = dlg_listbox_index(td->modelist, dlg);
943 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
946 /* Construct new entry */
948 str = dlg_editbox_get(td->valbox, dlg);
949 val = dupprintf("%c%s", type, str);
951 conf_set_str_str(conf, CONF_ttymodes, key, val);
953 dlg_refresh(td->listbox, dlg);
956 } else if (ctrl == td->rembutton) {
959 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
960 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
962 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
963 if (dlg_listbox_issel(td->listbox, dlg, i)) {
965 /* Populate controls with entry we're about to
966 * delete, for ease of editing.
967 * (If multiple entries were selected, don't
968 * touch the controls.) */
971 while (ttymodes[ind]) {
972 if (!strcmp(ttymodes[ind], key))
976 dlg_listbox_select(td->modelist, dlg, ind);
977 dlg_radiobutton_set(td->valradio, dlg,
979 dlg_editbox_set(td->valbox, dlg, val+1);
981 conf_del_str_str(conf, CONF_ttymodes, key);
985 dlg_refresh(td->listbox, dlg);
990 struct environ_data {
991 union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
994 static void environ_handler(union control *ctrl, void *dlg,
995 void *data, int event)
997 Conf *conf = (Conf *)data;
998 struct environ_data *ed =
999 (struct environ_data *)ctrl->generic.context.p;
1001 if (event == EVENT_REFRESH) {
1002 if (ctrl == ed->listbox) {
1004 dlg_update_start(ctrl, dlg);
1005 dlg_listbox_clear(ctrl, dlg);
1006 for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1008 val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1009 char *p = dupprintf("%s\t%s", key, val);
1010 dlg_listbox_add(ctrl, dlg, p);
1013 dlg_update_done(ctrl, dlg);
1015 } else if (event == EVENT_ACTION) {
1016 if (ctrl == ed->addbutton) {
1017 char *key, *val, *str;
1018 key = dlg_editbox_get(ed->varbox, dlg);
1024 val = dlg_editbox_get(ed->valbox, dlg);
1031 conf_set_str_str(conf, CONF_environmt, key, val);
1032 str = dupcat(key, "\t", val, NULL);
1033 dlg_editbox_set(ed->varbox, dlg, "");
1034 dlg_editbox_set(ed->valbox, dlg, "");
1038 dlg_refresh(ed->listbox, dlg);
1039 } else if (ctrl == ed->rembutton) {
1040 int i = dlg_listbox_index(ed->listbox, dlg);
1046 key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
1048 /* Populate controls with the entry we're about to delete
1049 * for ease of editing */
1050 val = conf_get_str_str(conf, CONF_environmt, key);
1051 dlg_editbox_set(ed->varbox, dlg, key);
1052 dlg_editbox_set(ed->valbox, dlg, val);
1054 conf_del_str_str(conf, CONF_environmt, key);
1057 dlg_refresh(ed->listbox, dlg);
1062 struct portfwd_data {
1063 union control *addbutton, *rembutton, *listbox;
1064 union control *sourcebox, *destbox, *direction;
1066 union control *addressfamily;
1070 static void portfwd_handler(union control *ctrl, void *dlg,
1071 void *data, int event)
1073 Conf *conf = (Conf *)data;
1074 struct portfwd_data *pfd =
1075 (struct portfwd_data *)ctrl->generic.context.p;
1077 if (event == EVENT_REFRESH) {
1078 if (ctrl == pfd->listbox) {
1080 dlg_update_start(ctrl, dlg);
1081 dlg_listbox_clear(ctrl, dlg);
1082 for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
1084 val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
1086 if (!strcmp(val, "D")) {
1089 * A dynamic forwarding is stored as L12345=D or
1090 * 6L12345=D (since it's mutually exclusive with
1091 * L12345=anything else), but displayed as D12345
1092 * to match the fiction that 'Local', 'Remote' and
1093 * 'Dynamic' are three distinct modes and also to
1094 * align with OpenSSH's command line option syntax
1095 * that people will already be used to. So, for
1096 * display purposes, find the L in the key string
1097 * and turn it into a D.
1099 p = dupprintf("%s\t", key);
1103 p = dupprintf("%s\t%s", key, val);
1104 dlg_listbox_add(ctrl, dlg, p);
1107 dlg_update_done(ctrl, dlg);
1108 } else if (ctrl == pfd->direction) {
1112 dlg_radiobutton_set(ctrl, dlg, 0);
1114 } else if (ctrl == pfd->addressfamily) {
1115 dlg_radiobutton_set(ctrl, dlg, 0);
1118 } else if (event == EVENT_ACTION) {
1119 if (ctrl == pfd->addbutton) {
1120 char *family, *type, *src, *key, *val;
1124 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1125 if (whichbutton == 1)
1127 else if (whichbutton == 2)
1133 whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1134 if (whichbutton == 0)
1136 else if (whichbutton == 1)
1141 src = dlg_editbox_get(pfd->sourcebox, dlg);
1143 dlg_error_msg(dlg, "You need to specify a source port number");
1148 val = dlg_editbox_get(pfd->destbox, dlg);
1149 if (!*val || !host_strchr(val, ':')) {
1151 "You need to specify a destination address\n"
1152 "in the form \"host.name:port\"");
1159 val = dupstr("D"); /* special case */
1162 key = dupcat(family, type, src, NULL);
1165 if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1166 dlg_error_msg(dlg, "Specified forwarding already exists");
1168 conf_set_str_str(conf, CONF_portfwd, key, val);
1173 dlg_refresh(pfd->listbox, dlg);
1174 } else if (ctrl == pfd->rembutton) {
1175 int i = dlg_listbox_index(pfd->listbox, dlg);
1179 char *key, *val, *p;
1181 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1183 static const char *const afs = "A46";
1184 static const char *const dirs = "LRD";
1191 /* Populate controls with the entry we're about to delete
1192 * for ease of editing */
1195 afp = strchr(afs, *p);
1197 idx = afp ? afp-afs : 0;
1202 dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1207 val = conf_get_str_str(conf, CONF_portfwd, key);
1208 if (!strcmp(val, "D")) {
1213 dlg_radiobutton_set(pfd->direction, dlg,
1214 strchr(dirs, dir) - dirs);
1217 dlg_editbox_set(pfd->sourcebox, dlg, p);
1218 dlg_editbox_set(pfd->destbox, dlg, val);
1220 conf_del_str_str(conf, CONF_portfwd, key);
1223 dlg_refresh(pfd->listbox, dlg);
1228 struct manual_hostkey_data {
1229 union control *addbutton, *rembutton, *listbox, *keybox;
1232 static void manual_hostkey_handler(union control *ctrl, void *dlg,
1233 void *data, int event)
1235 Conf *conf = (Conf *)data;
1236 struct manual_hostkey_data *mh =
1237 (struct manual_hostkey_data *)ctrl->generic.context.p;
1239 if (event == EVENT_REFRESH) {
1240 if (ctrl == mh->listbox) {
1242 dlg_update_start(ctrl, dlg);
1243 dlg_listbox_clear(ctrl, dlg);
1244 for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1247 val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1249 dlg_listbox_add(ctrl, dlg, key);
1251 dlg_update_done(ctrl, dlg);
1253 } else if (event == EVENT_ACTION) {
1254 if (ctrl == mh->addbutton) {
1257 key = dlg_editbox_get(mh->keybox, dlg);
1259 dlg_error_msg(dlg, "You need to specify a host key or "
1265 if (!validate_manual_hostkey(key)) {
1266 dlg_error_msg(dlg, "Host key is not in a valid format");
1267 } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
1269 dlg_error_msg(dlg, "Specified host key is already listed");
1271 conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
1275 dlg_refresh(mh->listbox, dlg);
1276 } else if (ctrl == mh->rembutton) {
1277 int i = dlg_listbox_index(mh->listbox, dlg);
1283 key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
1285 dlg_editbox_set(mh->keybox, dlg, key);
1287 conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
1290 dlg_refresh(mh->listbox, dlg);
1295 void setup_config_box(struct controlbox *b, int midsession,
1296 int protocol, int protcfginfo)
1298 struct controlset *s;
1299 struct sessionsaver_data *ssd;
1300 struct charclass_data *ccd;
1301 struct colour_data *cd;
1302 struct ttymodes_data *td;
1303 struct environ_data *ed;
1304 struct portfwd_data *pfd;
1305 struct manual_hostkey_data *mh;
1309 ssd = (struct sessionsaver_data *)
1310 ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data),
1311 sessionsaver_data_free);
1312 memset(ssd, 0, sizeof(*ssd));
1313 ssd->savedsession = dupstr("");
1314 ssd->midsession = midsession;
1317 * The standard panel that appears at the bottom of all panels:
1318 * Open, Cancel, Apply etc.
1320 s = ctrl_getset(b, "", "", "");
1321 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1322 ssd->okbutton = ctrl_pushbutton(s,
1323 (midsession ? "Apply" : "Open"),
1324 (char)(midsession ? 'a' : 'o'),
1326 sessionsaver_handler, P(ssd));
1327 ssd->okbutton->button.isdefault = TRUE;
1328 ssd->okbutton->generic.column = 3;
1329 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1330 sessionsaver_handler, P(ssd));
1331 ssd->cancelbutton->button.iscancel = TRUE;
1332 ssd->cancelbutton->generic.column = 4;
1333 /* We carefully don't close the 5-column part, so that platform-
1334 * specific add-ons can put extra buttons alongside Open and Cancel. */
1337 * The Session panel.
1339 str = dupprintf("Basic options for your %s session", appname);
1340 ctrl_settitle(b, "Session", str);
1344 struct hostport *hp = (struct hostport *)
1345 ctrl_alloc(b, sizeof(struct hostport));
1347 s = ctrl_getset(b, "Session", "hostport",
1348 "Specify the destination you want to connect to");
1349 ctrl_columns(s, 2, 75, 25);
1350 c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1351 HELPCTX(session_hostname),
1352 config_host_handler, I(0), I(0));
1353 c->generic.column = 0;
1355 c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1356 HELPCTX(session_hostname),
1357 config_port_handler, I(0), I(0));
1358 c->generic.column = 1;
1360 ctrl_columns(s, 1, 100);
1362 if (!backend_from_proto(PROT_SSH)) {
1363 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1364 HELPCTX(session_hostname),
1365 config_protocolbuttons_handler, P(hp),
1366 "Raw", 'w', I(PROT_RAW),
1367 "Telnet", 't', I(PROT_TELNET),
1368 "Rlogin", 'i', I(PROT_RLOGIN),
1371 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1372 HELPCTX(session_hostname),
1373 config_protocolbuttons_handler, P(hp),
1374 "Raw", 'w', I(PROT_RAW),
1375 "Telnet", 't', I(PROT_TELNET),
1376 "Rlogin", 'i', I(PROT_RLOGIN),
1377 "SSH", 's', I(PROT_SSH),
1383 * The Load/Save panel is available even in mid-session.
1385 s = ctrl_getset(b, "Session", "savedsessions",
1386 midsession ? "Save the current session settings" :
1387 "Load, save or delete a stored session");
1388 ctrl_columns(s, 2, 75, 25);
1389 get_sesslist(&ssd->sesslist, TRUE);
1390 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1391 HELPCTX(session_saved),
1392 sessionsaver_handler, P(ssd), P(NULL));
1393 ssd->editbox->generic.column = 0;
1394 /* Reset columns so that the buttons are alongside the list, rather
1395 * than alongside that edit box. */
1396 ctrl_columns(s, 1, 100);
1397 ctrl_columns(s, 2, 75, 25);
1398 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1399 HELPCTX(session_saved),
1400 sessionsaver_handler, P(ssd));
1401 ssd->listbox->generic.column = 0;
1402 ssd->listbox->listbox.height = 7;
1404 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1405 HELPCTX(session_saved),
1406 sessionsaver_handler, P(ssd));
1407 ssd->loadbutton->generic.column = 1;
1409 /* We can't offer the Load button mid-session, as it would allow the
1410 * user to load and subsequently save settings they can't see. (And
1411 * also change otherwise immutable settings underfoot; that probably
1412 * shouldn't be a problem, but.) */
1413 ssd->loadbutton = NULL;
1415 /* "Save" button is permitted mid-session. */
1416 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1417 HELPCTX(session_saved),
1418 sessionsaver_handler, P(ssd));
1419 ssd->savebutton->generic.column = 1;
1421 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1422 HELPCTX(session_saved),
1423 sessionsaver_handler, P(ssd));
1424 ssd->delbutton->generic.column = 1;
1426 /* Disable the Delete button mid-session too, for UI consistency. */
1427 ssd->delbutton = NULL;
1429 ctrl_columns(s, 1, 100);
1431 s = ctrl_getset(b, "Session", "otheropts", NULL);
1432 ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1433 HELPCTX(session_coe),
1434 conf_radiobutton_handler,
1435 I(CONF_close_on_exit),
1436 "Always", I(FORCE_ON),
1437 "Never", I(FORCE_OFF),
1438 "Only on clean exit", I(AUTO), NULL);
1441 * The Session/Logging panel.
1443 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1445 s = ctrl_getset(b, "Session/Logging", "main", NULL);
1447 * The logging buttons change depending on whether SSH packet
1448 * logging can sensibly be available.
1451 char *sshlogname, *sshrawlogname;
1452 if ((midsession && protocol == PROT_SSH) ||
1453 (!midsession && backend_from_proto(PROT_SSH))) {
1454 sshlogname = "SSH packets";
1455 sshrawlogname = "SSH packets and raw data";
1457 sshlogname = NULL; /* this will disable both buttons */
1458 sshrawlogname = NULL; /* this will just placate optimisers */
1460 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1461 HELPCTX(logging_main),
1462 loggingbuttons_handler,
1464 "None", 't', I(LGTYP_NONE),
1465 "Printable output", 'p', I(LGTYP_ASCII),
1466 "All session output", 'l', I(LGTYP_DEBUG),
1467 sshlogname, 's', I(LGTYP_PACKETS),
1468 sshrawlogname, 'r', I(LGTYP_SSHRAW),
1471 ctrl_filesel(s, "Log file name:", 'f',
1472 NULL, TRUE, "Select session log file name",
1473 HELPCTX(logging_filename),
1474 conf_filesel_handler, I(CONF_logfilename));
1475 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1476 " &T for time, and &H for host name)",
1477 HELPCTX(logging_filename));
1478 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1479 HELPCTX(logging_exists),
1480 conf_radiobutton_handler, I(CONF_logxfovr),
1481 "Always overwrite it", I(LGXF_OVR),
1482 "Always append to the end of it", I(LGXF_APN),
1483 "Ask the user every time", I(LGXF_ASK), NULL);
1484 ctrl_checkbox(s, "Flush log file frequently", 'u',
1485 HELPCTX(logging_flush),
1486 conf_checkbox_handler, I(CONF_logflush));
1488 if ((midsession && protocol == PROT_SSH) ||
1489 (!midsession && backend_from_proto(PROT_SSH))) {
1490 s = ctrl_getset(b, "Session/Logging", "ssh",
1491 "Options specific to SSH packet logging");
1492 ctrl_checkbox(s, "Omit known password fields", 'k',
1493 HELPCTX(logging_ssh_omit_password),
1494 conf_checkbox_handler, I(CONF_logomitpass));
1495 ctrl_checkbox(s, "Omit session data", 'd',
1496 HELPCTX(logging_ssh_omit_data),
1497 conf_checkbox_handler, I(CONF_logomitdata));
1501 * The Terminal panel.
1503 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1505 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1506 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1507 HELPCTX(terminal_autowrap),
1508 conf_checkbox_handler, I(CONF_wrap_mode));
1509 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1510 HELPCTX(terminal_decom),
1511 conf_checkbox_handler, I(CONF_dec_om));
1512 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1513 HELPCTX(terminal_lfhascr),
1514 conf_checkbox_handler, I(CONF_lfhascr));
1515 ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1516 HELPCTX(terminal_crhaslf),
1517 conf_checkbox_handler, I(CONF_crhaslf));
1518 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1519 HELPCTX(terminal_bce),
1520 conf_checkbox_handler, I(CONF_bce));
1521 ctrl_checkbox(s, "Enable blinking text", 'n',
1522 HELPCTX(terminal_blink),
1523 conf_checkbox_handler, I(CONF_blinktext));
1524 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1525 HELPCTX(terminal_answerback),
1526 conf_editbox_handler, I(CONF_answerback), I(1));
1528 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1529 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1530 HELPCTX(terminal_localecho),
1531 conf_radiobutton_handler,I(CONF_localecho),
1533 "Force on", I(FORCE_ON),
1534 "Force off", I(FORCE_OFF), NULL);
1535 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1536 HELPCTX(terminal_localedit),
1537 conf_radiobutton_handler,I(CONF_localedit),
1539 "Force on", I(FORCE_ON),
1540 "Force off", I(FORCE_OFF), NULL);
1542 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1543 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1544 HELPCTX(terminal_printing),
1545 printerbox_handler, P(NULL), P(NULL));
1548 * The Terminal/Keyboard panel.
1550 ctrl_settitle(b, "Terminal/Keyboard",
1551 "Options controlling the effects of keys");
1553 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1554 "Change the sequences sent by:");
1555 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1556 HELPCTX(keyboard_backspace),
1557 conf_radiobutton_handler,
1558 I(CONF_bksp_is_delete),
1559 "Control-H", I(0), "Control-? (127)", I(1), NULL);
1560 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1561 HELPCTX(keyboard_homeend),
1562 conf_radiobutton_handler,
1563 I(CONF_rxvt_homeend),
1564 "Standard", I(0), "rxvt", I(1), NULL);
1565 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1566 HELPCTX(keyboard_funkeys),
1567 conf_radiobutton_handler,
1569 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1570 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1572 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1573 "Application keypad settings:");
1574 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1575 HELPCTX(keyboard_appcursor),
1576 conf_radiobutton_handler,
1578 "Normal", I(0), "Application", I(1), NULL);
1579 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1580 HELPCTX(keyboard_appkeypad),
1581 numeric_keypad_handler, P(NULL),
1582 "Normal", I(0), "Application", I(1), "NetHack", I(2),
1586 * The Terminal/Bell panel.
1588 ctrl_settitle(b, "Terminal/Bell",
1589 "Options controlling the terminal bell");
1591 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1592 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1593 HELPCTX(bell_style),
1594 conf_radiobutton_handler, I(CONF_beep),
1595 "None (bell disabled)", I(BELL_DISABLED),
1596 "Make default system alert sound", I(BELL_DEFAULT),
1597 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1599 s = ctrl_getset(b, "Terminal/Bell", "overload",
1600 "Control the bell overload behaviour");
1601 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1602 HELPCTX(bell_overload),
1603 conf_checkbox_handler, I(CONF_bellovl));
1604 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1605 HELPCTX(bell_overload),
1606 conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1607 ctrl_editbox(s, "... in this many seconds", 't', 20,
1608 HELPCTX(bell_overload),
1609 conf_editbox_handler, I(CONF_bellovl_t),
1611 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1612 HELPCTX(bell_overload));
1613 ctrl_editbox(s, "Seconds of silence required", 's', 20,
1614 HELPCTX(bell_overload),
1615 conf_editbox_handler, I(CONF_bellovl_s),
1619 * The Terminal/Features panel.
1621 ctrl_settitle(b, "Terminal/Features",
1622 "Enabling and disabling advanced terminal features");
1624 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1625 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1626 HELPCTX(features_application),
1627 conf_checkbox_handler, I(CONF_no_applic_c));
1628 ctrl_checkbox(s, "Disable application keypad mode", 'k',
1629 HELPCTX(features_application),
1630 conf_checkbox_handler, I(CONF_no_applic_k));
1631 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1632 HELPCTX(features_mouse),
1633 conf_checkbox_handler, I(CONF_no_mouse_rep));
1634 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1635 HELPCTX(features_resize),
1636 conf_checkbox_handler,
1637 I(CONF_no_remote_resize));
1638 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1639 HELPCTX(features_altscreen),
1640 conf_checkbox_handler, I(CONF_no_alt_screen));
1641 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1642 HELPCTX(features_retitle),
1643 conf_checkbox_handler,
1644 I(CONF_no_remote_wintitle));
1645 ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1646 HELPCTX(features_qtitle),
1647 conf_radiobutton_handler,
1648 I(CONF_remote_qtitle_action),
1649 "None", I(TITLE_NONE),
1650 "Empty string", I(TITLE_EMPTY),
1651 "Window title", I(TITLE_REAL), NULL);
1652 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1653 HELPCTX(features_dbackspace),
1654 conf_checkbox_handler, I(CONF_no_dbackspace));
1655 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1656 'r', HELPCTX(features_charset), conf_checkbox_handler,
1657 I(CONF_no_remote_charset));
1658 ctrl_checkbox(s, "Disable Arabic text shaping",
1659 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1660 I(CONF_arabicshaping));
1661 ctrl_checkbox(s, "Disable bidirectional text display",
1662 'd', HELPCTX(features_bidi), conf_checkbox_handler,
1668 str = dupprintf("Options controlling %s's window", appname);
1669 ctrl_settitle(b, "Window", str);
1672 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1673 ctrl_columns(s, 2, 50, 50);
1674 c = ctrl_editbox(s, "Columns", 'm', 100,
1675 HELPCTX(window_size),
1676 conf_editbox_handler, I(CONF_width), I(-1));
1677 c->generic.column = 0;
1678 c = ctrl_editbox(s, "Rows", 'r', 100,
1679 HELPCTX(window_size),
1680 conf_editbox_handler, I(CONF_height),I(-1));
1681 c->generic.column = 1;
1682 ctrl_columns(s, 1, 100);
1684 s = ctrl_getset(b, "Window", "scrollback",
1685 "Control the scrollback in the window");
1686 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1687 HELPCTX(window_scrollback),
1688 conf_editbox_handler, I(CONF_savelines), I(-1));
1689 ctrl_checkbox(s, "Display scrollbar", 'd',
1690 HELPCTX(window_scrollback),
1691 conf_checkbox_handler, I(CONF_scrollbar));
1692 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1693 HELPCTX(window_scrollback),
1694 conf_checkbox_handler, I(CONF_scroll_on_key));
1695 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1696 HELPCTX(window_scrollback),
1697 conf_checkbox_handler, I(CONF_scroll_on_disp));
1698 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1699 HELPCTX(window_erased),
1700 conf_checkbox_handler,
1701 I(CONF_erase_to_scrollback));
1704 * The Window/Appearance panel.
1706 str = dupprintf("Configure the appearance of %s's window", appname);
1707 ctrl_settitle(b, "Window/Appearance", str);
1710 s = ctrl_getset(b, "Window/Appearance", "cursor",
1711 "Adjust the use of the cursor");
1712 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1713 HELPCTX(appearance_cursor),
1714 conf_radiobutton_handler,
1715 I(CONF_cursor_type),
1717 "Underline", 'u', I(1),
1718 "Vertical line", 'v', I(2), NULL);
1719 ctrl_checkbox(s, "Cursor blinks", 'b',
1720 HELPCTX(appearance_cursor),
1721 conf_checkbox_handler, I(CONF_blink_cur));
1723 s = ctrl_getset(b, "Window/Appearance", "font",
1725 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1726 HELPCTX(appearance_font),
1727 conf_fontsel_handler, I(CONF_font));
1729 s = ctrl_getset(b, "Window/Appearance", "mouse",
1730 "Adjust the use of the mouse pointer");
1731 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1732 HELPCTX(appearance_hidemouse),
1733 conf_checkbox_handler, I(CONF_hide_mouseptr));
1735 s = ctrl_getset(b, "Window/Appearance", "border",
1736 "Adjust the window border");
1737 ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1738 HELPCTX(appearance_border),
1739 conf_editbox_handler,
1740 I(CONF_window_border), I(-1));
1743 * The Window/Behaviour panel.
1745 str = dupprintf("Configure the behaviour of %s's window", appname);
1746 ctrl_settitle(b, "Window/Behaviour", str);
1749 s = ctrl_getset(b, "Window/Behaviour", "title",
1750 "Adjust the behaviour of the window title");
1751 ctrl_editbox(s, "Window title:", 't', 100,
1752 HELPCTX(appearance_title),
1753 conf_editbox_handler, I(CONF_wintitle), I(1));
1754 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1755 HELPCTX(appearance_title),
1756 conf_checkbox_handler,
1757 I(CHECKBOX_INVERT | CONF_win_name_always));
1759 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1760 ctrl_checkbox(s, "Warn before closing window", 'w',
1761 HELPCTX(behaviour_closewarn),
1762 conf_checkbox_handler, I(CONF_warn_on_close));
1765 * The Window/Translation panel.
1767 ctrl_settitle(b, "Window/Translation",
1768 "Options controlling character set translation");
1770 s = ctrl_getset(b, "Window/Translation", "trans",
1771 "Character set translation");
1772 ctrl_combobox(s, "Remote character set:",
1773 'r', 100, HELPCTX(translation_codepage),
1774 codepage_handler, P(NULL), P(NULL));
1776 s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1777 ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1778 HELPCTX(translation_cjk_ambig_wide),
1779 conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1781 str = dupprintf("Adjust how %s handles line drawing characters", appname);
1782 s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1784 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1785 HELPCTX(translation_linedraw),
1786 conf_radiobutton_handler,
1788 "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1789 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1791 ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1792 HELPCTX(selection_linedraw),
1793 conf_checkbox_handler, I(CONF_rawcnp));
1796 * The Window/Selection panel.
1798 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1800 s = ctrl_getset(b, "Window/Selection", "mouse",
1801 "Control use of mouse");
1802 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1803 HELPCTX(selection_shiftdrag),
1804 conf_checkbox_handler, I(CONF_mouse_override));
1805 ctrl_radiobuttons(s,
1806 "Default selection mode (Alt+drag does the other one):",
1808 HELPCTX(selection_rect),
1809 conf_radiobutton_handler,
1810 I(CONF_rect_select),
1811 "Normal", 'n', I(0),
1812 "Rectangular block", 'r', I(1), NULL);
1814 s = ctrl_getset(b, "Window/Selection", "charclass",
1815 "Control the select-one-word-at-a-time mode");
1816 ccd = (struct charclass_data *)
1817 ctrl_alloc(b, sizeof(struct charclass_data));
1818 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1819 HELPCTX(selection_charclasses),
1820 charclass_handler, P(ccd));
1821 ccd->listbox->listbox.multisel = 1;
1822 ccd->listbox->listbox.ncols = 4;
1823 ccd->listbox->listbox.percentages = snewn(4, int);
1824 ccd->listbox->listbox.percentages[0] = 15;
1825 ccd->listbox->listbox.percentages[1] = 25;
1826 ccd->listbox->listbox.percentages[2] = 20;
1827 ccd->listbox->listbox.percentages[3] = 40;
1828 ctrl_columns(s, 2, 67, 33);
1829 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1830 HELPCTX(selection_charclasses),
1831 charclass_handler, P(ccd), P(NULL));
1832 ccd->editbox->generic.column = 0;
1833 ccd->button = ctrl_pushbutton(s, "Set", 's',
1834 HELPCTX(selection_charclasses),
1835 charclass_handler, P(ccd));
1836 ccd->button->generic.column = 1;
1837 ctrl_columns(s, 1, 100);
1840 * The Window/Colours panel.
1842 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1844 s = ctrl_getset(b, "Window/Colours", "general",
1845 "General options for colour usage");
1846 ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1847 HELPCTX(colours_ansi),
1848 conf_checkbox_handler, I(CONF_ansi_colour));
1849 ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1850 HELPCTX(colours_xterm256), conf_checkbox_handler,
1851 I(CONF_xterm_256_colour));
1852 ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
1853 HELPCTX(colours_bold),
1854 conf_radiobutton_handler, I(CONF_bold_style),
1860 str = dupprintf("Adjust the precise colours %s displays", appname);
1861 s = ctrl_getset(b, "Window/Colours", "adjust", str);
1863 ctrl_text(s, "Select a colour from the list, and then click the"
1864 " Modify button to change its appearance.",
1865 HELPCTX(colours_config));
1866 ctrl_columns(s, 2, 67, 33);
1867 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1868 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1869 HELPCTX(colours_config), colour_handler, P(cd));
1870 cd->listbox->generic.column = 0;
1871 cd->listbox->listbox.height = 7;
1872 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1873 c->generic.column = 1;
1874 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1875 colour_handler, P(cd), P(NULL));
1876 cd->redit->generic.column = 1;
1877 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1878 colour_handler, P(cd), P(NULL));
1879 cd->gedit->generic.column = 1;
1880 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1881 colour_handler, P(cd), P(NULL));
1882 cd->bedit->generic.column = 1;
1883 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1884 colour_handler, P(cd));
1885 cd->button->generic.column = 1;
1886 ctrl_columns(s, 1, 100);
1889 * The Connection panel. This doesn't show up if we're in a
1890 * non-network utility such as pterm. We tell this by being
1891 * passed a protocol < 0.
1893 if (protocol >= 0) {
1894 ctrl_settitle(b, "Connection", "Options controlling the connection");
1896 s = ctrl_getset(b, "Connection", "keepalive",
1897 "Sending of null packets to keep session active");
1898 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1899 HELPCTX(connection_keepalive),
1900 conf_editbox_handler, I(CONF_ping_interval),
1904 s = ctrl_getset(b, "Connection", "tcp",
1905 "Low-level TCP connection options");
1906 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1907 'n', HELPCTX(connection_nodelay),
1908 conf_checkbox_handler,
1909 I(CONF_tcp_nodelay));
1910 ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1911 'p', HELPCTX(connection_tcpkeepalive),
1912 conf_checkbox_handler,
1913 I(CONF_tcp_keepalives));
1915 s = ctrl_getset(b, "Connection", "ipversion",
1916 "Internet protocol version");
1917 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1918 HELPCTX(connection_ipversion),
1919 conf_radiobutton_handler,
1920 I(CONF_addressfamily),
1921 "Auto", 'u', I(ADDRTYPE_UNSPEC),
1922 "IPv4", '4', I(ADDRTYPE_IPV4),
1923 "IPv6", '6', I(ADDRTYPE_IPV6),
1928 char *label = backend_from_proto(PROT_SSH) ?
1929 "Logical name of remote host (e.g. for SSH key lookup):" :
1930 "Logical name of remote host:";
1931 s = ctrl_getset(b, "Connection", "identity",
1932 "Logical name of remote host");
1933 ctrl_editbox(s, label, 'm', 100,
1934 HELPCTX(connection_loghost),
1935 conf_editbox_handler, I(CONF_loghost), I(1));
1940 * A sub-panel Connection/Data, containing options that
1941 * decide on data to send to the server.
1944 ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1946 s = ctrl_getset(b, "Connection/Data", "login",
1948 ctrl_editbox(s, "Auto-login username", 'u', 50,
1949 HELPCTX(connection_username),
1950 conf_editbox_handler, I(CONF_username), I(1));
1952 /* We assume the local username is sufficiently stable
1953 * to include on the dialog box. */
1954 char *user = get_username();
1955 char *userlabel = dupprintf("Use system username (%s)",
1958 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1959 HELPCTX(connection_username_from_env),
1960 conf_radiobutton_handler,
1961 I(CONF_username_from_env),
1968 s = ctrl_getset(b, "Connection/Data", "term",
1969 "Terminal details");
1970 ctrl_editbox(s, "Terminal-type string", 't', 50,
1971 HELPCTX(connection_termtype),
1972 conf_editbox_handler, I(CONF_termtype), I(1));
1973 ctrl_editbox(s, "Terminal speeds", 's', 50,
1974 HELPCTX(connection_termspeed),
1975 conf_editbox_handler, I(CONF_termspeed), I(1));
1977 s = ctrl_getset(b, "Connection/Data", "env",
1978 "Environment variables");
1979 ctrl_columns(s, 2, 80, 20);
1980 ed = (struct environ_data *)
1981 ctrl_alloc(b, sizeof(struct environ_data));
1982 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1983 HELPCTX(telnet_environ),
1984 environ_handler, P(ed), P(NULL));
1985 ed->varbox->generic.column = 0;
1986 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1987 HELPCTX(telnet_environ),
1988 environ_handler, P(ed), P(NULL));
1989 ed->valbox->generic.column = 0;
1990 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1991 HELPCTX(telnet_environ),
1992 environ_handler, P(ed));
1993 ed->addbutton->generic.column = 1;
1994 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1995 HELPCTX(telnet_environ),
1996 environ_handler, P(ed));
1997 ed->rembutton->generic.column = 1;
1998 ctrl_columns(s, 1, 100);
1999 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2000 HELPCTX(telnet_environ),
2001 environ_handler, P(ed));
2002 ed->listbox->listbox.height = 3;
2003 ed->listbox->listbox.ncols = 2;
2004 ed->listbox->listbox.percentages = snewn(2, int);
2005 ed->listbox->listbox.percentages[0] = 30;
2006 ed->listbox->listbox.percentages[1] = 70;
2013 * The Connection/Proxy panel.
2015 ctrl_settitle(b, "Connection/Proxy",
2016 "Options controlling proxy usage");
2018 s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
2019 ctrl_radiobuttons(s, "Proxy type:", 't', 3,
2020 HELPCTX(proxy_type),
2021 conf_radiobutton_handler,
2023 "None", I(PROXY_NONE),
2024 "SOCKS 4", I(PROXY_SOCKS4),
2025 "SOCKS 5", I(PROXY_SOCKS5),
2026 "HTTP", I(PROXY_HTTP),
2027 "Telnet", I(PROXY_TELNET),
2029 ctrl_columns(s, 2, 80, 20);
2030 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
2031 HELPCTX(proxy_main),
2032 conf_editbox_handler,
2033 I(CONF_proxy_host), I(1));
2034 c->generic.column = 0;
2035 c = ctrl_editbox(s, "Port", 'p', 100,
2036 HELPCTX(proxy_main),
2037 conf_editbox_handler,
2040 c->generic.column = 1;
2041 ctrl_columns(s, 1, 100);
2042 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
2043 HELPCTX(proxy_exclude),
2044 conf_editbox_handler,
2045 I(CONF_proxy_exclude_list), I(1));
2046 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
2047 HELPCTX(proxy_exclude),
2048 conf_checkbox_handler,
2049 I(CONF_even_proxy_localhost));
2050 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
2052 conf_radiobutton_handler,
2056 "Yes", I(FORCE_ON), NULL);
2057 ctrl_editbox(s, "Username", 'u', 60,
2058 HELPCTX(proxy_auth),
2059 conf_editbox_handler,
2060 I(CONF_proxy_username), I(1));
2061 c = ctrl_editbox(s, "Password", 'w', 60,
2062 HELPCTX(proxy_auth),
2063 conf_editbox_handler,
2064 I(CONF_proxy_password), I(1));
2065 c->editbox.password = 1;
2066 ctrl_editbox(s, "Telnet command", 'm', 100,
2067 HELPCTX(proxy_command),
2068 conf_editbox_handler,
2069 I(CONF_proxy_telnet_command), I(1));
2073 * The Telnet panel exists in the base config box, and in a
2074 * mid-session reconfig box _if_ we're using Telnet.
2076 if (!midsession || protocol == PROT_TELNET) {
2078 * The Connection/Telnet panel.
2080 ctrl_settitle(b, "Connection/Telnet",
2081 "Options controlling Telnet connections");
2083 s = ctrl_getset(b, "Connection/Telnet", "protocol",
2084 "Telnet protocol adjustments");
2087 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2089 HELPCTX(telnet_oldenviron),
2090 conf_radiobutton_handler,
2091 I(CONF_rfc_environ),
2092 "BSD (commonplace)", 'b', I(0),
2093 "RFC 1408 (unusual)", 'f', I(1), NULL);
2094 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2095 HELPCTX(telnet_passive),
2096 conf_radiobutton_handler,
2097 I(CONF_passive_telnet),
2098 "Passive", I(1), "Active", I(0), NULL);
2100 ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2101 HELPCTX(telnet_specialkeys),
2102 conf_checkbox_handler,
2103 I(CONF_telnet_keyboard));
2104 ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2105 'm', HELPCTX(telnet_newline),
2106 conf_checkbox_handler,
2107 I(CONF_telnet_newline));
2113 * The Connection/Rlogin panel.
2115 ctrl_settitle(b, "Connection/Rlogin",
2116 "Options controlling Rlogin connections");
2118 s = ctrl_getset(b, "Connection/Rlogin", "data",
2119 "Data to send to the server");
2120 ctrl_editbox(s, "Local username:", 'l', 50,
2121 HELPCTX(rlogin_localuser),
2122 conf_editbox_handler, I(CONF_localusername), I(1));
2127 * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2128 * when we're not doing SSH.
2131 if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2134 * The Connection/SSH panel.
2136 ctrl_settitle(b, "Connection/SSH",
2137 "Options controlling SSH connections");
2139 /* SSH-1 or connection-sharing downstream */
2140 if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
2141 s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2142 ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2143 "session; it is only here so that sub-panels of it can "
2144 "exist without looking strange.", HELPCTX(no_help));
2149 s = ctrl_getset(b, "Connection/SSH", "data",
2150 "Data to send to the server");
2151 ctrl_editbox(s, "Remote command:", 'r', 100,
2152 HELPCTX(ssh_command),
2153 conf_editbox_handler, I(CONF_remote_cmd), I(1));
2155 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2156 ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2157 HELPCTX(ssh_noshell),
2158 conf_checkbox_handler,
2159 I(CONF_ssh_no_shell));
2162 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2163 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2165 ctrl_checkbox(s, "Enable compression", 'e',
2166 HELPCTX(ssh_compress),
2167 conf_checkbox_handler,
2168 I(CONF_compression));
2172 s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
2174 ctrl_checkbox(s, "Share SSH connections if possible", 's',
2176 conf_checkbox_handler,
2177 I(CONF_ssh_connection_sharing));
2179 ctrl_text(s, "Permitted roles in a shared connection:",
2180 HELPCTX(ssh_share));
2181 ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
2183 conf_checkbox_handler,
2184 I(CONF_ssh_connection_sharing_upstream));
2185 ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
2187 conf_checkbox_handler,
2188 I(CONF_ssh_connection_sharing_downstream));
2192 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2194 ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2195 HELPCTX(ssh_protocol),
2196 conf_radiobutton_handler,
2198 "1 only", 'l', I(0),
2201 "2 only", 'y', I(3), NULL);
2205 * The Connection/SSH/Kex panel. (Owing to repeat key
2206 * exchange, much of this is meaningful in mid-session _if_
2207 * we're using SSH-2 and are not a connection-sharing
2208 * downstream, or haven't decided yet.)
2210 if (protcfginfo != 1 && protcfginfo != -1) {
2211 ctrl_settitle(b, "Connection/SSH/Kex",
2212 "Options controlling SSH key exchange");
2214 s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2215 "Key exchange algorithm options");
2216 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2217 HELPCTX(ssh_kexlist),
2218 kexlist_handler, P(NULL));
2219 c->listbox.height = 5;
2221 s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2222 "Options controlling key re-exchange");
2224 ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2225 HELPCTX(ssh_kex_repeat),
2226 conf_editbox_handler,
2227 I(CONF_ssh_rekey_time),
2229 ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2230 HELPCTX(ssh_kex_repeat),
2231 conf_editbox_handler,
2232 I(CONF_ssh_rekey_data),
2234 ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2235 HELPCTX(ssh_kex_repeat));
2239 * Manual host key configuration is irrelevant mid-session,
2240 * as we enforce that the host key for rekeys is the
2241 * same as that used at the start of the session.
2244 s = ctrl_getset(b, "Connection/SSH/Kex", "hostkeys",
2245 "Manually configure host keys for this connection");
2247 ctrl_columns(s, 2, 75, 25);
2248 c = ctrl_text(s, "Host keys or fingerprints to accept:",
2249 HELPCTX(ssh_kex_manual_hostkeys));
2250 c->generic.column = 0;
2251 /* You want to select from the list, _then_ hit Remove. So
2252 * tab order should be that way round. */
2253 mh = (struct manual_hostkey_data *)
2254 ctrl_alloc(b,sizeof(struct manual_hostkey_data));
2255 mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2256 HELPCTX(ssh_kex_manual_hostkeys),
2257 manual_hostkey_handler, P(mh));
2258 mh->rembutton->generic.column = 1;
2259 mh->rembutton->generic.tabdelay = 1;
2260 mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2261 HELPCTX(ssh_kex_manual_hostkeys),
2262 manual_hostkey_handler, P(mh));
2263 /* This list box can't be very tall, because there's not
2264 * much room in the pane on Windows at least. This makes
2265 * it become really unhelpful if a horizontal scrollbar
2266 * appears, so we suppress that. */
2267 mh->listbox->listbox.height = 2;
2268 mh->listbox->listbox.hscroll = FALSE;
2269 ctrl_tabdelay(s, mh->rembutton);
2270 mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
2271 HELPCTX(ssh_kex_manual_hostkeys),
2272 manual_hostkey_handler, P(mh), P(NULL));
2273 mh->keybox->generic.column = 0;
2274 mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
2275 HELPCTX(ssh_kex_manual_hostkeys),
2276 manual_hostkey_handler, P(mh));
2277 mh->addbutton->generic.column = 1;
2278 ctrl_columns(s, 1, 100);
2281 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2283 * The Connection/SSH/Cipher panel.
2285 ctrl_settitle(b, "Connection/SSH/Cipher",
2286 "Options controlling SSH encryption");
2288 s = ctrl_getset(b, "Connection/SSH/Cipher",
2289 "encryption", "Encryption options");
2290 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2291 HELPCTX(ssh_ciphers),
2292 cipherlist_handler, P(NULL));
2293 c->listbox.height = 6;
2295 ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2296 HELPCTX(ssh_ciphers),
2297 conf_checkbox_handler,
2298 I(CONF_ssh2_des_cbc));
2304 * The Connection/SSH/Auth panel.
2306 ctrl_settitle(b, "Connection/SSH/Auth",
2307 "Options controlling SSH authentication");
2309 s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2310 ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2311 HELPCTX(ssh_auth_bypass),
2312 conf_checkbox_handler,
2313 I(CONF_ssh_no_userauth));
2314 ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2315 'd', HELPCTX(ssh_auth_banner),
2316 conf_checkbox_handler,
2317 I(CONF_ssh_show_banner));
2319 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2320 "Authentication methods");
2321 ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2322 HELPCTX(ssh_auth_pageant),
2323 conf_checkbox_handler,
2325 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2326 HELPCTX(ssh_auth_tis),
2327 conf_checkbox_handler,
2328 I(CONF_try_tis_auth));
2329 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2330 'i', HELPCTX(ssh_auth_ki),
2331 conf_checkbox_handler,
2332 I(CONF_try_ki_auth));
2334 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2335 "Authentication parameters");
2336 ctrl_checkbox(s, "Allow agent forwarding", 'f',
2337 HELPCTX(ssh_auth_agentfwd),
2338 conf_checkbox_handler, I(CONF_agentfwd));
2339 ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2340 HELPCTX(ssh_auth_changeuser),
2341 conf_checkbox_handler,
2342 I(CONF_change_username));
2343 ctrl_filesel(s, "Private key file for authentication:", 'k',
2344 FILTER_KEY_FILES, FALSE, "Select private key file",
2345 HELPCTX(ssh_auth_privkey),
2346 conf_filesel_handler, I(CONF_keyfile));
2350 * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2351 * the main Auth panel.
2353 ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2354 "Options controlling GSSAPI authentication");
2355 s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2357 ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2358 't', HELPCTX(ssh_gssapi),
2359 conf_checkbox_handler,
2360 I(CONF_try_gssapi_auth));
2362 ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2363 HELPCTX(ssh_gssapi_delegation),
2364 conf_checkbox_handler,
2368 * GSSAPI library selection.
2371 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2372 'p', HELPCTX(ssh_gssapi_libraries),
2373 gsslist_handler, P(NULL));
2374 c->listbox.height = ngsslibs;
2377 * I currently assume that if more than one GSS
2378 * library option is available, then one of them is
2379 * 'user-supplied' and so we should present the
2380 * following file selector. This is at least half-
2381 * reasonable, because if we're using statically
2382 * linked GSSAPI then there will only be one option
2383 * and no way to load from a user-supplied library,
2384 * whereas if we're using dynamic libraries then
2385 * there will almost certainly be some default
2386 * option in addition to a user-supplied path. If
2387 * anyone ever ports PuTTY to a system on which
2388 * dynamic-library GSSAPI is available but there is
2389 * absolutely no consensus on where to keep the
2390 * libraries, there'll need to be a flag alongside
2391 * ngsslibs to control whether the file selector is
2395 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2396 FILTER_DYNLIB_FILES, FALSE, "Select library file",
2397 HELPCTX(ssh_gssapi_libraries),
2398 conf_filesel_handler,
2399 I(CONF_ssh_gss_custom));
2406 * The Connection/SSH/TTY panel.
2408 ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2410 s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2411 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2413 conf_checkbox_handler,
2416 s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2418 td = (struct ttymodes_data *)
2419 ctrl_alloc(b, sizeof(struct ttymodes_data));
2420 ctrl_columns(s, 2, 75, 25);
2421 c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2422 c->generic.column = 0;
2423 td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2424 HELPCTX(ssh_ttymodes),
2425 ttymodes_handler, P(td));
2426 td->rembutton->generic.column = 1;
2427 td->rembutton->generic.tabdelay = 1;
2428 ctrl_columns(s, 1, 100);
2429 td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2430 HELPCTX(ssh_ttymodes),
2431 ttymodes_handler, P(td));
2432 td->listbox->listbox.multisel = 1;
2433 td->listbox->listbox.height = 4;
2434 td->listbox->listbox.ncols = 2;
2435 td->listbox->listbox.percentages = snewn(2, int);
2436 td->listbox->listbox.percentages[0] = 40;
2437 td->listbox->listbox.percentages[1] = 60;
2438 ctrl_tabdelay(s, td->rembutton);
2439 ctrl_columns(s, 2, 75, 25);
2440 td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2441 HELPCTX(ssh_ttymodes),
2442 ttymodes_handler, P(td));
2443 td->modelist->generic.column = 0;
2444 td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2445 HELPCTX(ssh_ttymodes),
2446 ttymodes_handler, P(td));
2447 td->addbutton->generic.column = 1;
2448 td->addbutton->generic.tabdelay = 1;
2449 ctrl_columns(s, 1, 100); /* column break */
2450 /* Bit of a hack to get the value radio buttons and
2451 * edit-box on the same row. */
2452 ctrl_columns(s, 3, 25, 50, 25);
2453 c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2454 c->generic.column = 0;
2455 td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2456 HELPCTX(ssh_ttymodes),
2457 ttymodes_handler, P(td),
2458 "Auto", NO_SHORTCUT, P(NULL),
2459 "This:", NO_SHORTCUT, P(NULL),
2461 td->valradio->generic.column = 1;
2462 td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2463 HELPCTX(ssh_ttymodes),
2464 ttymodes_handler, P(td), P(NULL));
2465 td->valbox->generic.column = 2;
2466 ctrl_tabdelay(s, td->addbutton);
2472 * The Connection/SSH/X11 panel.
2474 ctrl_settitle(b, "Connection/SSH/X11",
2475 "Options controlling SSH X11 forwarding");
2477 s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2478 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2479 HELPCTX(ssh_tunnels_x11),
2480 conf_checkbox_handler,I(CONF_x11_forward));
2481 ctrl_editbox(s, "X display location", 'x', 50,
2482 HELPCTX(ssh_tunnels_x11),
2483 conf_editbox_handler, I(CONF_x11_display), I(1));
2484 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2485 HELPCTX(ssh_tunnels_x11auth),
2486 conf_radiobutton_handler,
2488 "MIT-Magic-Cookie-1", I(X11_MIT),
2489 "XDM-Authorization-1", I(X11_XDM), NULL);
2493 * The Tunnels panel _is_ still available in mid-session.
2495 ctrl_settitle(b, "Connection/SSH/Tunnels",
2496 "Options controlling SSH port forwarding");
2498 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2500 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2501 HELPCTX(ssh_tunnels_portfwd_localhost),
2502 conf_checkbox_handler,
2503 I(CONF_lport_acceptall));
2504 ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2505 HELPCTX(ssh_tunnels_portfwd_localhost),
2506 conf_checkbox_handler,
2507 I(CONF_rport_acceptall));
2509 ctrl_columns(s, 3, 55, 20, 25);
2510 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2511 c->generic.column = COLUMN_FIELD(0,2);
2512 /* You want to select from the list, _then_ hit Remove. So tab order
2513 * should be that way round. */
2514 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2515 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2516 HELPCTX(ssh_tunnels_portfwd),
2517 portfwd_handler, P(pfd));
2518 pfd->rembutton->generic.column = 2;
2519 pfd->rembutton->generic.tabdelay = 1;
2520 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2521 HELPCTX(ssh_tunnels_portfwd),
2522 portfwd_handler, P(pfd));
2523 pfd->listbox->listbox.height = 3;
2524 pfd->listbox->listbox.ncols = 2;
2525 pfd->listbox->listbox.percentages = snewn(2, int);
2526 pfd->listbox->listbox.percentages[0] = 20;
2527 pfd->listbox->listbox.percentages[1] = 80;
2528 ctrl_tabdelay(s, pfd->rembutton);
2529 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2530 /* You want to enter source, destination and type, _then_ hit Add.
2531 * Again, we adjust the tab order to reflect this. */
2532 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2533 HELPCTX(ssh_tunnels_portfwd),
2534 portfwd_handler, P(pfd));
2535 pfd->addbutton->generic.column = 2;
2536 pfd->addbutton->generic.tabdelay = 1;
2537 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2538 HELPCTX(ssh_tunnels_portfwd),
2539 portfwd_handler, P(pfd), P(NULL));
2540 pfd->sourcebox->generic.column = 0;
2541 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2542 HELPCTX(ssh_tunnels_portfwd),
2543 portfwd_handler, P(pfd), P(NULL));
2544 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2545 HELPCTX(ssh_tunnels_portfwd),
2546 portfwd_handler, P(pfd),
2547 "Local", 'l', P(NULL),
2548 "Remote", 'm', P(NULL),
2549 "Dynamic", 'y', P(NULL),
2552 pfd->addressfamily =
2553 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2554 HELPCTX(ssh_tunnels_portfwd_ipversion),
2555 portfwd_handler, P(pfd),
2556 "Auto", 'u', I(ADDRTYPE_UNSPEC),
2557 "IPv4", '4', I(ADDRTYPE_IPV4),
2558 "IPv6", '6', I(ADDRTYPE_IPV6),
2561 ctrl_tabdelay(s, pfd->addbutton);
2562 ctrl_columns(s, 1, 100);
2566 * The Connection/SSH/Bugs panel.
2568 ctrl_settitle(b, "Connection/SSH/Bugs",
2569 "Workarounds for SSH server bugs");
2571 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2572 "Detection of known bugs in SSH servers");
2573 ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2574 HELPCTX(ssh_bugs_ignore1),
2575 sshbug_handler, I(CONF_sshbug_ignore1));
2576 ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2577 HELPCTX(ssh_bugs_plainpw1),
2578 sshbug_handler, I(CONF_sshbug_plainpw1));
2579 ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2580 HELPCTX(ssh_bugs_rsa1),
2581 sshbug_handler, I(CONF_sshbug_rsa1));
2582 ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2583 HELPCTX(ssh_bugs_ignore2),
2584 sshbug_handler, I(CONF_sshbug_ignore2));
2585 ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
2586 20, HELPCTX(ssh_bugs_winadj),
2587 sshbug_handler, I(CONF_sshbug_winadj));
2588 ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2589 HELPCTX(ssh_bugs_hmac2),
2590 sshbug_handler, I(CONF_sshbug_hmac2));
2591 ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2592 HELPCTX(ssh_bugs_derivekey2),
2593 sshbug_handler, I(CONF_sshbug_derivekey2));
2594 ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2595 HELPCTX(ssh_bugs_rsapad2),
2596 sshbug_handler, I(CONF_sshbug_rsapad2));
2597 ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2598 HELPCTX(ssh_bugs_pksessid2),
2599 sshbug_handler, I(CONF_sshbug_pksessid2));
2600 ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2601 HELPCTX(ssh_bugs_rekey2),
2602 sshbug_handler, I(CONF_sshbug_rekey2));
2603 ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2604 HELPCTX(ssh_bugs_maxpkt2),
2605 sshbug_handler, I(CONF_sshbug_maxpkt2));
2606 ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
2607 HELPCTX(ssh_bugs_chanreq),
2608 sshbug_handler, I(CONF_sshbug_chanreq));