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 { const char *s; int c; } ciphers[] = {
360 { "ChaCha20 (SSH-2 only)", CIPHER_CHACHA20 },
361 { "3DES", CIPHER_3DES },
362 { "Blowfish", CIPHER_BLOWFISH },
363 { "DES", CIPHER_DES },
364 { "AES (SSH-2 only)", CIPHER_AES },
365 { "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
366 { "-- warn below here --", CIPHER_WARN }
369 /* Set up the "selected ciphers" box. */
370 /* (cipherlist assumed to contain all ciphers) */
371 dlg_update_start(ctrl, dlg);
372 dlg_listbox_clear(ctrl, dlg);
373 for (i = 0; i < CIPHER_MAX; i++) {
374 int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
376 const char *cstr = NULL;
377 for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
378 if (ciphers[j].c == c) {
383 dlg_listbox_addwithid(ctrl, dlg, cstr, c);
385 dlg_update_done(ctrl, dlg);
387 } else if (event == EVENT_VALCHANGE) {
390 /* Update array to match the list box. */
391 for (i=0; i < CIPHER_MAX; i++)
392 conf_set_int_int(conf, CONF_ssh_cipherlist, i,
393 dlg_listbox_getid(ctrl, dlg, i));
398 static void gsslist_handler(union control *ctrl, void *dlg,
399 void *data, int event)
401 Conf *conf = (Conf *)data;
402 if (event == EVENT_REFRESH) {
405 dlg_update_start(ctrl, dlg);
406 dlg_listbox_clear(ctrl, dlg);
407 for (i = 0; i < ngsslibs; i++) {
408 int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
409 assert(id >= 0 && id < ngsslibs);
410 dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
412 dlg_update_done(ctrl, dlg);
414 } else if (event == EVENT_VALCHANGE) {
417 /* Update array to match the list box. */
418 for (i=0; i < ngsslibs; i++)
419 conf_set_int_int(conf, CONF_ssh_gsslist, i,
420 dlg_listbox_getid(ctrl, dlg, i));
425 static void kexlist_handler(union control *ctrl, void *dlg,
426 void *data, int event)
428 Conf *conf = (Conf *)data;
429 if (event == EVENT_REFRESH) {
432 static const struct { const char *s; int k; } kexes[] = {
433 { "Diffie-Hellman group 1", KEX_DHGROUP1 },
434 { "Diffie-Hellman group 14", KEX_DHGROUP14 },
435 { "Diffie-Hellman group exchange", KEX_DHGEX },
436 { "RSA-based key exchange", KEX_RSA },
437 { "ECDH key exchange", KEX_ECDH },
438 { "-- warn below here --", KEX_WARN }
441 /* Set up the "kex preference" box. */
442 /* (kexlist assumed to contain all algorithms) */
443 dlg_update_start(ctrl, dlg);
444 dlg_listbox_clear(ctrl, dlg);
445 for (i = 0; i < KEX_MAX; i++) {
446 int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
448 const char *kstr = NULL;
449 for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
450 if (kexes[j].k == k) {
455 dlg_listbox_addwithid(ctrl, dlg, kstr, k);
457 dlg_update_done(ctrl, dlg);
459 } else if (event == EVENT_VALCHANGE) {
462 /* Update array to match the list box. */
463 for (i=0; i < KEX_MAX; i++)
464 conf_set_int_int(conf, CONF_ssh_kexlist, i,
465 dlg_listbox_getid(ctrl, dlg, i));
469 static void printerbox_handler(union control *ctrl, void *dlg,
470 void *data, int event)
472 Conf *conf = (Conf *)data;
473 if (event == EVENT_REFRESH) {
478 dlg_update_start(ctrl, dlg);
480 * Some backends may wish to disable the drop-down list on
481 * this edit box. Be prepared for this.
483 if (ctrl->editbox.has_list) {
484 dlg_listbox_clear(ctrl, dlg);
485 dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
486 pe = printer_start_enum(&nprinters);
487 for (i = 0; i < nprinters; i++)
488 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
489 printer_finish_enum(pe);
491 printer = conf_get_str(conf, CONF_printer);
493 printer = PRINTER_DISABLED_STRING;
494 dlg_editbox_set(ctrl, dlg, printer);
495 dlg_update_done(ctrl, dlg);
496 } else if (event == EVENT_VALCHANGE) {
497 char *printer = dlg_editbox_get(ctrl, dlg);
498 if (!strcmp(printer, PRINTER_DISABLED_STRING))
500 conf_set_str(conf, CONF_printer, printer);
505 static void codepage_handler(union control *ctrl, void *dlg,
506 void *data, int event)
508 Conf *conf = (Conf *)data;
509 if (event == EVENT_REFRESH) {
511 const char *cp, *thiscp;
512 dlg_update_start(ctrl, dlg);
513 thiscp = cp_name(decode_codepage(conf_get_str(conf,
514 CONF_line_codepage)));
515 dlg_listbox_clear(ctrl, dlg);
516 for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
517 dlg_listbox_add(ctrl, dlg, cp);
518 dlg_editbox_set(ctrl, dlg, thiscp);
519 conf_set_str(conf, CONF_line_codepage, thiscp);
520 dlg_update_done(ctrl, dlg);
521 } else if (event == EVENT_VALCHANGE) {
522 char *codepage = dlg_editbox_get(ctrl, dlg);
523 conf_set_str(conf, CONF_line_codepage,
524 cp_name(decode_codepage(codepage)));
529 static void sshbug_handler(union control *ctrl, void *dlg,
530 void *data, int event)
532 Conf *conf = (Conf *)data;
533 if (event == EVENT_REFRESH) {
535 * We must fetch the previously configured value from the Conf
536 * before we start modifying the drop-down list, otherwise the
537 * spurious SELCHANGE we trigger in the process will overwrite
538 * the value we wanted to keep.
540 int oldconf = conf_get_int(conf, ctrl->listbox.context.i);
541 dlg_update_start(ctrl, dlg);
542 dlg_listbox_clear(ctrl, dlg);
543 dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
544 dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
545 dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
547 case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
548 case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
549 case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
551 dlg_update_done(ctrl, dlg);
552 } else if (event == EVENT_SELCHANGE) {
553 int i = dlg_listbox_index(ctrl, dlg);
557 i = dlg_listbox_getid(ctrl, dlg, i);
558 conf_set_int(conf, ctrl->listbox.context.i, i);
562 struct sessionsaver_data {
563 union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
564 union control *okbutton, *cancelbutton;
565 struct sesslist sesslist;
567 char *savedsession; /* the current contents of ssd->editbox */
570 static void sessionsaver_data_free(void *ssdv)
572 struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
573 get_sesslist(&ssd->sesslist, FALSE);
574 sfree(ssd->savedsession);
579 * Helper function to load the session selected in the list box, if
580 * any, as this is done in more than one place below. Returns 0 for
583 static int load_selected_session(struct sessionsaver_data *ssd,
584 void *dlg, Conf *conf, int *maybe_launch)
586 int i = dlg_listbox_index(ssd->listbox, dlg);
592 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
593 load_settings(ssd->sesslist.sessions[i], conf);
594 sfree(ssd->savedsession);
595 ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]);
597 *maybe_launch = !isdef;
598 dlg_refresh(NULL, dlg);
599 /* Restore the selection, which might have been clobbered by
600 * changing the value of the edit box. */
601 dlg_listbox_select(ssd->listbox, dlg, i);
605 static void sessionsaver_handler(union control *ctrl, void *dlg,
606 void *data, int event)
608 Conf *conf = (Conf *)data;
609 struct sessionsaver_data *ssd =
610 (struct sessionsaver_data *)ctrl->generic.context.p;
612 if (event == EVENT_REFRESH) {
613 if (ctrl == ssd->editbox) {
614 dlg_editbox_set(ctrl, dlg, ssd->savedsession);
615 } else if (ctrl == ssd->listbox) {
617 dlg_update_start(ctrl, dlg);
618 dlg_listbox_clear(ctrl, dlg);
619 for (i = 0; i < ssd->sesslist.nsessions; i++)
620 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
621 dlg_update_done(ctrl, dlg);
623 } else if (event == EVENT_VALCHANGE) {
624 int top, bottom, halfway, i;
625 if (ctrl == ssd->editbox) {
626 sfree(ssd->savedsession);
627 ssd->savedsession = dlg_editbox_get(ctrl, dlg);
628 top = ssd->sesslist.nsessions;
630 while (top-bottom > 1) {
631 halfway = (top+bottom)/2;
632 i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]);
639 if (top == ssd->sesslist.nsessions) {
642 dlg_listbox_select(ssd->listbox, dlg, top);
644 } else if (event == EVENT_ACTION) {
646 if (!ssd->midsession &&
647 (ctrl == ssd->listbox ||
648 (ssd->loadbutton && ctrl == ssd->loadbutton))) {
650 * The user has double-clicked a session, or hit Load.
651 * We must load the selected session, and then
652 * terminate the configuration dialog _if_ there was a
653 * double-click on the list box _and_ that session
654 * contains a hostname.
656 if (load_selected_session(ssd, dlg, conf, &mbl) &&
657 (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
658 dlg_end(dlg, 1); /* it's all over, and succeeded */
660 } else if (ctrl == ssd->savebutton) {
661 int isdef = !strcmp(ssd->savedsession, "Default Settings");
662 if (!ssd->savedsession[0]) {
663 int i = dlg_listbox_index(ssd->listbox, dlg);
668 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
669 sfree(ssd->savedsession);
670 ssd->savedsession = dupstr(isdef ? "" :
671 ssd->sesslist.sessions[i]);
674 char *errmsg = save_settings(ssd->savedsession, conf);
676 dlg_error_msg(dlg, errmsg);
680 get_sesslist(&ssd->sesslist, FALSE);
681 get_sesslist(&ssd->sesslist, TRUE);
682 dlg_refresh(ssd->editbox, dlg);
683 dlg_refresh(ssd->listbox, dlg);
684 } else if (!ssd->midsession &&
685 ssd->delbutton && ctrl == ssd->delbutton) {
686 int i = dlg_listbox_index(ssd->listbox, dlg);
690 del_settings(ssd->sesslist.sessions[i]);
691 get_sesslist(&ssd->sesslist, FALSE);
692 get_sesslist(&ssd->sesslist, TRUE);
693 dlg_refresh(ssd->listbox, dlg);
695 } else if (ctrl == ssd->okbutton) {
696 if (ssd->midsession) {
697 /* In a mid-session Change Settings, Apply is always OK. */
702 * Annoying special case. If the `Open' button is
703 * pressed while no host name is currently set, _and_
704 * the session list previously had the focus, _and_
705 * there was a session selected in that which had a
706 * valid host name in it, then load it and go.
708 if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
709 !conf_launchable(conf)) {
710 Conf *conf2 = conf_new();
712 if (!load_selected_session(ssd, dlg, conf2, &mbl)) {
717 /* If at this point we have a valid session, go! */
718 if (mbl && conf_launchable(conf2)) {
719 conf_copy_into(conf, conf2);
729 * Otherwise, do the normal thing: if we have a valid
730 * session, get going.
732 if (conf_launchable(conf)) {
736 } else if (ctrl == ssd->cancelbutton) {
742 struct charclass_data {
743 union control *listbox, *editbox, *button;
746 static void charclass_handler(union control *ctrl, void *dlg,
747 void *data, int event)
749 Conf *conf = (Conf *)data;
750 struct charclass_data *ccd =
751 (struct charclass_data *)ctrl->generic.context.p;
753 if (event == EVENT_REFRESH) {
754 if (ctrl == ccd->listbox) {
756 dlg_update_start(ctrl, dlg);
757 dlg_listbox_clear(ctrl, dlg);
758 for (i = 0; i < 128; i++) {
760 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
761 (i >= 0x21 && i != 0x7F) ? i : ' ',
762 conf_get_int_int(conf, CONF_wordness, i));
763 dlg_listbox_add(ctrl, dlg, str);
765 dlg_update_done(ctrl, dlg);
767 } else if (event == EVENT_ACTION) {
768 if (ctrl == ccd->button) {
771 str = dlg_editbox_get(ccd->editbox, dlg);
774 for (i = 0; i < 128; i++) {
775 if (dlg_listbox_issel(ccd->listbox, dlg, i))
776 conf_set_int_int(conf, CONF_wordness, i, n);
778 dlg_refresh(ccd->listbox, dlg);
784 union control *listbox, *redit, *gedit, *bedit, *button;
787 static const char *const colours[] = {
788 "Default Foreground", "Default Bold Foreground",
789 "Default Background", "Default Bold Background",
790 "Cursor Text", "Cursor Colour",
791 "ANSI Black", "ANSI Black Bold",
792 "ANSI Red", "ANSI Red Bold",
793 "ANSI Green", "ANSI Green Bold",
794 "ANSI Yellow", "ANSI Yellow Bold",
795 "ANSI Blue", "ANSI Blue Bold",
796 "ANSI Magenta", "ANSI Magenta Bold",
797 "ANSI Cyan", "ANSI Cyan Bold",
798 "ANSI White", "ANSI White Bold"
801 static void colour_handler(union control *ctrl, void *dlg,
802 void *data, int event)
804 Conf *conf = (Conf *)data;
805 struct colour_data *cd =
806 (struct colour_data *)ctrl->generic.context.p;
807 int update = FALSE, clear = FALSE, r, g, b;
809 if (event == EVENT_REFRESH) {
810 if (ctrl == cd->listbox) {
812 dlg_update_start(ctrl, dlg);
813 dlg_listbox_clear(ctrl, dlg);
814 for (i = 0; i < lenof(colours); i++)
815 dlg_listbox_add(ctrl, dlg, colours[i]);
816 dlg_update_done(ctrl, dlg);
820 } else if (event == EVENT_SELCHANGE) {
821 if (ctrl == cd->listbox) {
822 /* The user has selected a colour. Update the RGB text. */
823 int i = dlg_listbox_index(ctrl, dlg);
828 r = conf_get_int_int(conf, CONF_colours, i*3+0);
829 g = conf_get_int_int(conf, CONF_colours, i*3+1);
830 b = conf_get_int_int(conf, CONF_colours, i*3+2);
834 } else if (event == EVENT_VALCHANGE) {
835 if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
836 /* The user has changed the colour using the edit boxes. */
840 str = dlg_editbox_get(ctrl, dlg);
843 if (cval > 255) cval = 255;
844 if (cval < 0) cval = 0;
846 i = dlg_listbox_index(cd->listbox, dlg);
848 if (ctrl == cd->redit)
849 conf_set_int_int(conf, CONF_colours, i*3+0, cval);
850 else if (ctrl == cd->gedit)
851 conf_set_int_int(conf, CONF_colours, i*3+1, cval);
852 else if (ctrl == cd->bedit)
853 conf_set_int_int(conf, CONF_colours, i*3+2, cval);
856 } else if (event == EVENT_ACTION) {
857 if (ctrl == cd->button) {
858 int i = dlg_listbox_index(cd->listbox, dlg);
864 * Start a colour selector, which will send us an
865 * EVENT_CALLBACK when it's finished and allow us to
866 * pick up the results.
868 dlg_coloursel_start(ctrl, dlg,
869 conf_get_int_int(conf, CONF_colours, i*3+0),
870 conf_get_int_int(conf, CONF_colours, i*3+1),
871 conf_get_int_int(conf, CONF_colours, i*3+2));
873 } else if (event == EVENT_CALLBACK) {
874 if (ctrl == cd->button) {
875 int i = dlg_listbox_index(cd->listbox, dlg);
877 * Collect the results of the colour selector. Will
878 * return nonzero on success, or zero if the colour
879 * selector did nothing (user hit Cancel, for example).
881 if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
882 conf_set_int_int(conf, CONF_colours, i*3+0, r);
883 conf_set_int_int(conf, CONF_colours, i*3+1, g);
884 conf_set_int_int(conf, CONF_colours, i*3+2, b);
893 dlg_editbox_set(cd->redit, dlg, "");
894 dlg_editbox_set(cd->gedit, dlg, "");
895 dlg_editbox_set(cd->bedit, dlg, "");
898 sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
899 sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
900 sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
905 struct ttymodes_data {
906 union control *modelist, *valradio, *valbox;
907 union control *addbutton, *rembutton, *listbox;
910 static void ttymodes_handler(union control *ctrl, void *dlg,
911 void *data, int event)
913 Conf *conf = (Conf *)data;
914 struct ttymodes_data *td =
915 (struct ttymodes_data *)ctrl->generic.context.p;
917 if (event == EVENT_REFRESH) {
918 if (ctrl == td->listbox) {
920 dlg_update_start(ctrl, dlg);
921 dlg_listbox_clear(ctrl, dlg);
922 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
924 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
925 char *disp = dupprintf("%s\t%s", key,
926 (val[0] == 'A') ? "(auto)" : val+1);
927 dlg_listbox_add(ctrl, dlg, disp);
930 dlg_update_done(ctrl, dlg);
931 } else if (ctrl == td->modelist) {
933 dlg_update_start(ctrl, dlg);
934 dlg_listbox_clear(ctrl, dlg);
935 for (i = 0; ttymodes[i]; i++)
936 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
937 dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
938 dlg_update_done(ctrl, dlg);
939 } else if (ctrl == td->valradio) {
940 dlg_radiobutton_set(ctrl, dlg, 0);
942 } else if (event == EVENT_ACTION) {
943 if (ctrl == td->addbutton) {
944 int ind = dlg_listbox_index(td->modelist, dlg);
946 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
949 /* Construct new entry */
951 str = dlg_editbox_get(td->valbox, dlg);
952 val = dupprintf("%c%s", type, str);
954 conf_set_str_str(conf, CONF_ttymodes, key, val);
956 dlg_refresh(td->listbox, dlg);
959 } else if (ctrl == td->rembutton) {
962 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
963 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
965 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
966 if (dlg_listbox_issel(td->listbox, dlg, i)) {
968 /* Populate controls with entry we're about to
969 * delete, for ease of editing.
970 * (If multiple entries were selected, don't
971 * touch the controls.) */
974 while (ttymodes[ind]) {
975 if (!strcmp(ttymodes[ind], key))
979 dlg_listbox_select(td->modelist, dlg, ind);
980 dlg_radiobutton_set(td->valradio, dlg,
982 dlg_editbox_set(td->valbox, dlg, val+1);
984 conf_del_str_str(conf, CONF_ttymodes, key);
988 dlg_refresh(td->listbox, dlg);
993 struct environ_data {
994 union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
997 static void environ_handler(union control *ctrl, void *dlg,
998 void *data, int event)
1000 Conf *conf = (Conf *)data;
1001 struct environ_data *ed =
1002 (struct environ_data *)ctrl->generic.context.p;
1004 if (event == EVENT_REFRESH) {
1005 if (ctrl == ed->listbox) {
1007 dlg_update_start(ctrl, dlg);
1008 dlg_listbox_clear(ctrl, dlg);
1009 for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1011 val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1012 char *p = dupprintf("%s\t%s", key, val);
1013 dlg_listbox_add(ctrl, dlg, p);
1016 dlg_update_done(ctrl, dlg);
1018 } else if (event == EVENT_ACTION) {
1019 if (ctrl == ed->addbutton) {
1020 char *key, *val, *str;
1021 key = dlg_editbox_get(ed->varbox, dlg);
1027 val = dlg_editbox_get(ed->valbox, dlg);
1034 conf_set_str_str(conf, CONF_environmt, key, val);
1035 str = dupcat(key, "\t", val, NULL);
1036 dlg_editbox_set(ed->varbox, dlg, "");
1037 dlg_editbox_set(ed->valbox, dlg, "");
1041 dlg_refresh(ed->listbox, dlg);
1042 } else if (ctrl == ed->rembutton) {
1043 int i = dlg_listbox_index(ed->listbox, dlg);
1049 key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
1051 /* Populate controls with the entry we're about to delete
1052 * for ease of editing */
1053 val = conf_get_str_str(conf, CONF_environmt, key);
1054 dlg_editbox_set(ed->varbox, dlg, key);
1055 dlg_editbox_set(ed->valbox, dlg, val);
1057 conf_del_str_str(conf, CONF_environmt, key);
1060 dlg_refresh(ed->listbox, dlg);
1065 struct portfwd_data {
1066 union control *addbutton, *rembutton, *listbox;
1067 union control *sourcebox, *destbox, *direction;
1069 union control *addressfamily;
1073 static void portfwd_handler(union control *ctrl, void *dlg,
1074 void *data, int event)
1076 Conf *conf = (Conf *)data;
1077 struct portfwd_data *pfd =
1078 (struct portfwd_data *)ctrl->generic.context.p;
1080 if (event == EVENT_REFRESH) {
1081 if (ctrl == pfd->listbox) {
1083 dlg_update_start(ctrl, dlg);
1084 dlg_listbox_clear(ctrl, dlg);
1085 for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
1087 val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
1089 if (!strcmp(val, "D")) {
1092 * A dynamic forwarding is stored as L12345=D or
1093 * 6L12345=D (since it's mutually exclusive with
1094 * L12345=anything else), but displayed as D12345
1095 * to match the fiction that 'Local', 'Remote' and
1096 * 'Dynamic' are three distinct modes and also to
1097 * align with OpenSSH's command line option syntax
1098 * that people will already be used to. So, for
1099 * display purposes, find the L in the key string
1100 * and turn it into a D.
1102 p = dupprintf("%s\t", key);
1106 p = dupprintf("%s\t%s", key, val);
1107 dlg_listbox_add(ctrl, dlg, p);
1110 dlg_update_done(ctrl, dlg);
1111 } else if (ctrl == pfd->direction) {
1115 dlg_radiobutton_set(ctrl, dlg, 0);
1117 } else if (ctrl == pfd->addressfamily) {
1118 dlg_radiobutton_set(ctrl, dlg, 0);
1121 } else if (event == EVENT_ACTION) {
1122 if (ctrl == pfd->addbutton) {
1123 const char *family, *type;
1124 char *src, *key, *val;
1128 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1129 if (whichbutton == 1)
1131 else if (whichbutton == 2)
1137 whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1138 if (whichbutton == 0)
1140 else if (whichbutton == 1)
1145 src = dlg_editbox_get(pfd->sourcebox, dlg);
1147 dlg_error_msg(dlg, "You need to specify a source port number");
1152 val = dlg_editbox_get(pfd->destbox, dlg);
1153 if (!*val || !host_strchr(val, ':')) {
1155 "You need to specify a destination address\n"
1156 "in the form \"host.name:port\"");
1163 val = dupstr("D"); /* special case */
1166 key = dupcat(family, type, src, NULL);
1169 if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1170 dlg_error_msg(dlg, "Specified forwarding already exists");
1172 conf_set_str_str(conf, CONF_portfwd, key, val);
1177 dlg_refresh(pfd->listbox, dlg);
1178 } else if (ctrl == pfd->rembutton) {
1179 int i = dlg_listbox_index(pfd->listbox, dlg);
1186 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1188 static const char *const afs = "A46";
1189 static const char *const dirs = "LRD";
1196 /* Populate controls with the entry we're about to delete
1197 * for ease of editing */
1200 afp = strchr(afs, *p);
1202 idx = afp ? afp-afs : 0;
1207 dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1212 val = conf_get_str_str(conf, CONF_portfwd, key);
1213 if (!strcmp(val, "D")) {
1218 dlg_radiobutton_set(pfd->direction, dlg,
1219 strchr(dirs, dir) - dirs);
1222 dlg_editbox_set(pfd->sourcebox, dlg, p);
1223 dlg_editbox_set(pfd->destbox, dlg, val);
1225 conf_del_str_str(conf, CONF_portfwd, key);
1228 dlg_refresh(pfd->listbox, dlg);
1233 struct manual_hostkey_data {
1234 union control *addbutton, *rembutton, *listbox, *keybox;
1237 static void manual_hostkey_handler(union control *ctrl, void *dlg,
1238 void *data, int event)
1240 Conf *conf = (Conf *)data;
1241 struct manual_hostkey_data *mh =
1242 (struct manual_hostkey_data *)ctrl->generic.context.p;
1244 if (event == EVENT_REFRESH) {
1245 if (ctrl == mh->listbox) {
1247 dlg_update_start(ctrl, dlg);
1248 dlg_listbox_clear(ctrl, dlg);
1249 for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1252 val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1254 dlg_listbox_add(ctrl, dlg, key);
1256 dlg_update_done(ctrl, dlg);
1258 } else if (event == EVENT_ACTION) {
1259 if (ctrl == mh->addbutton) {
1262 key = dlg_editbox_get(mh->keybox, dlg);
1264 dlg_error_msg(dlg, "You need to specify a host key or "
1270 if (!validate_manual_hostkey(key)) {
1271 dlg_error_msg(dlg, "Host key is not in a valid format");
1272 } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
1274 dlg_error_msg(dlg, "Specified host key is already listed");
1276 conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
1280 dlg_refresh(mh->listbox, dlg);
1281 } else if (ctrl == mh->rembutton) {
1282 int i = dlg_listbox_index(mh->listbox, dlg);
1288 key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
1290 dlg_editbox_set(mh->keybox, dlg, key);
1292 conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
1295 dlg_refresh(mh->listbox, dlg);
1300 void setup_config_box(struct controlbox *b, int midsession,
1301 int protocol, int protcfginfo)
1303 struct controlset *s;
1304 struct sessionsaver_data *ssd;
1305 struct charclass_data *ccd;
1306 struct colour_data *cd;
1307 struct ttymodes_data *td;
1308 struct environ_data *ed;
1309 struct portfwd_data *pfd;
1310 struct manual_hostkey_data *mh;
1314 ssd = (struct sessionsaver_data *)
1315 ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data),
1316 sessionsaver_data_free);
1317 memset(ssd, 0, sizeof(*ssd));
1318 ssd->savedsession = dupstr("");
1319 ssd->midsession = midsession;
1322 * The standard panel that appears at the bottom of all panels:
1323 * Open, Cancel, Apply etc.
1325 s = ctrl_getset(b, "", "", "");
1326 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1327 ssd->okbutton = ctrl_pushbutton(s,
1328 (midsession ? "Apply" : "Open"),
1329 (char)(midsession ? 'a' : 'o'),
1331 sessionsaver_handler, P(ssd));
1332 ssd->okbutton->button.isdefault = TRUE;
1333 ssd->okbutton->generic.column = 3;
1334 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1335 sessionsaver_handler, P(ssd));
1336 ssd->cancelbutton->button.iscancel = TRUE;
1337 ssd->cancelbutton->generic.column = 4;
1338 /* We carefully don't close the 5-column part, so that platform-
1339 * specific add-ons can put extra buttons alongside Open and Cancel. */
1342 * The Session panel.
1344 str = dupprintf("Basic options for your %s session", appname);
1345 ctrl_settitle(b, "Session", str);
1349 struct hostport *hp = (struct hostport *)
1350 ctrl_alloc(b, sizeof(struct hostport));
1352 s = ctrl_getset(b, "Session", "hostport",
1353 "Specify the destination you want to connect to");
1354 ctrl_columns(s, 2, 75, 25);
1355 c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1356 HELPCTX(session_hostname),
1357 config_host_handler, I(0), I(0));
1358 c->generic.column = 0;
1360 c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1361 HELPCTX(session_hostname),
1362 config_port_handler, I(0), I(0));
1363 c->generic.column = 1;
1365 ctrl_columns(s, 1, 100);
1367 if (!backend_from_proto(PROT_SSH)) {
1368 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1369 HELPCTX(session_hostname),
1370 config_protocolbuttons_handler, P(hp),
1371 "Raw", 'w', I(PROT_RAW),
1372 "Telnet", 't', I(PROT_TELNET),
1373 "Rlogin", 'i', I(PROT_RLOGIN),
1376 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1377 HELPCTX(session_hostname),
1378 config_protocolbuttons_handler, P(hp),
1379 "Raw", 'w', I(PROT_RAW),
1380 "Telnet", 't', I(PROT_TELNET),
1381 "Rlogin", 'i', I(PROT_RLOGIN),
1382 "SSH", 's', I(PROT_SSH),
1388 * The Load/Save panel is available even in mid-session.
1390 s = ctrl_getset(b, "Session", "savedsessions",
1391 midsession ? "Save the current session settings" :
1392 "Load, save or delete a stored session");
1393 ctrl_columns(s, 2, 75, 25);
1394 get_sesslist(&ssd->sesslist, TRUE);
1395 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1396 HELPCTX(session_saved),
1397 sessionsaver_handler, P(ssd), P(NULL));
1398 ssd->editbox->generic.column = 0;
1399 /* Reset columns so that the buttons are alongside the list, rather
1400 * than alongside that edit box. */
1401 ctrl_columns(s, 1, 100);
1402 ctrl_columns(s, 2, 75, 25);
1403 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1404 HELPCTX(session_saved),
1405 sessionsaver_handler, P(ssd));
1406 ssd->listbox->generic.column = 0;
1407 ssd->listbox->listbox.height = 7;
1409 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1410 HELPCTX(session_saved),
1411 sessionsaver_handler, P(ssd));
1412 ssd->loadbutton->generic.column = 1;
1414 /* We can't offer the Load button mid-session, as it would allow the
1415 * user to load and subsequently save settings they can't see. (And
1416 * also change otherwise immutable settings underfoot; that probably
1417 * shouldn't be a problem, but.) */
1418 ssd->loadbutton = NULL;
1420 /* "Save" button is permitted mid-session. */
1421 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1422 HELPCTX(session_saved),
1423 sessionsaver_handler, P(ssd));
1424 ssd->savebutton->generic.column = 1;
1426 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1427 HELPCTX(session_saved),
1428 sessionsaver_handler, P(ssd));
1429 ssd->delbutton->generic.column = 1;
1431 /* Disable the Delete button mid-session too, for UI consistency. */
1432 ssd->delbutton = NULL;
1434 ctrl_columns(s, 1, 100);
1436 s = ctrl_getset(b, "Session", "otheropts", NULL);
1437 ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1438 HELPCTX(session_coe),
1439 conf_radiobutton_handler,
1440 I(CONF_close_on_exit),
1441 "Always", I(FORCE_ON),
1442 "Never", I(FORCE_OFF),
1443 "Only on clean exit", I(AUTO), NULL);
1446 * The Session/Logging panel.
1448 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1450 s = ctrl_getset(b, "Session/Logging", "main", NULL);
1452 * The logging buttons change depending on whether SSH packet
1453 * logging can sensibly be available.
1456 const char *sshlogname, *sshrawlogname;
1457 if ((midsession && protocol == PROT_SSH) ||
1458 (!midsession && backend_from_proto(PROT_SSH))) {
1459 sshlogname = "SSH packets";
1460 sshrawlogname = "SSH packets and raw data";
1462 sshlogname = NULL; /* this will disable both buttons */
1463 sshrawlogname = NULL; /* this will just placate optimisers */
1465 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1466 HELPCTX(logging_main),
1467 loggingbuttons_handler,
1469 "None", 't', I(LGTYP_NONE),
1470 "Printable output", 'p', I(LGTYP_ASCII),
1471 "All session output", 'l', I(LGTYP_DEBUG),
1472 sshlogname, 's', I(LGTYP_PACKETS),
1473 sshrawlogname, 'r', I(LGTYP_SSHRAW),
1476 ctrl_filesel(s, "Log file name:", 'f',
1477 NULL, TRUE, "Select session log file name",
1478 HELPCTX(logging_filename),
1479 conf_filesel_handler, I(CONF_logfilename));
1480 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1481 " &T for time, &H for host name, and &P for port number)",
1482 HELPCTX(logging_filename));
1483 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1484 HELPCTX(logging_exists),
1485 conf_radiobutton_handler, I(CONF_logxfovr),
1486 "Always overwrite it", I(LGXF_OVR),
1487 "Always append to the end of it", I(LGXF_APN),
1488 "Ask the user every time", I(LGXF_ASK), NULL);
1489 ctrl_checkbox(s, "Flush log file frequently", 'u',
1490 HELPCTX(logging_flush),
1491 conf_checkbox_handler, I(CONF_logflush));
1493 if ((midsession && protocol == PROT_SSH) ||
1494 (!midsession && backend_from_proto(PROT_SSH))) {
1495 s = ctrl_getset(b, "Session/Logging", "ssh",
1496 "Options specific to SSH packet logging");
1497 ctrl_checkbox(s, "Omit known password fields", 'k',
1498 HELPCTX(logging_ssh_omit_password),
1499 conf_checkbox_handler, I(CONF_logomitpass));
1500 ctrl_checkbox(s, "Omit session data", 'd',
1501 HELPCTX(logging_ssh_omit_data),
1502 conf_checkbox_handler, I(CONF_logomitdata));
1506 * The Terminal panel.
1508 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1510 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1511 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1512 HELPCTX(terminal_autowrap),
1513 conf_checkbox_handler, I(CONF_wrap_mode));
1514 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1515 HELPCTX(terminal_decom),
1516 conf_checkbox_handler, I(CONF_dec_om));
1517 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1518 HELPCTX(terminal_lfhascr),
1519 conf_checkbox_handler, I(CONF_lfhascr));
1520 ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1521 HELPCTX(terminal_crhaslf),
1522 conf_checkbox_handler, I(CONF_crhaslf));
1523 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1524 HELPCTX(terminal_bce),
1525 conf_checkbox_handler, I(CONF_bce));
1526 ctrl_checkbox(s, "Enable blinking text", 'n',
1527 HELPCTX(terminal_blink),
1528 conf_checkbox_handler, I(CONF_blinktext));
1529 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1530 HELPCTX(terminal_answerback),
1531 conf_editbox_handler, I(CONF_answerback), I(1));
1533 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1534 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1535 HELPCTX(terminal_localecho),
1536 conf_radiobutton_handler,I(CONF_localecho),
1538 "Force on", I(FORCE_ON),
1539 "Force off", I(FORCE_OFF), NULL);
1540 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1541 HELPCTX(terminal_localedit),
1542 conf_radiobutton_handler,I(CONF_localedit),
1544 "Force on", I(FORCE_ON),
1545 "Force off", I(FORCE_OFF), NULL);
1547 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1548 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1549 HELPCTX(terminal_printing),
1550 printerbox_handler, P(NULL), P(NULL));
1553 * The Terminal/Keyboard panel.
1555 ctrl_settitle(b, "Terminal/Keyboard",
1556 "Options controlling the effects of keys");
1558 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1559 "Change the sequences sent by:");
1560 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1561 HELPCTX(keyboard_backspace),
1562 conf_radiobutton_handler,
1563 I(CONF_bksp_is_delete),
1564 "Control-H", I(0), "Control-? (127)", I(1), NULL);
1565 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1566 HELPCTX(keyboard_homeend),
1567 conf_radiobutton_handler,
1568 I(CONF_rxvt_homeend),
1569 "Standard", I(0), "rxvt", I(1), NULL);
1570 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1571 HELPCTX(keyboard_funkeys),
1572 conf_radiobutton_handler,
1574 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1575 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1577 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1578 "Application keypad settings:");
1579 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1580 HELPCTX(keyboard_appcursor),
1581 conf_radiobutton_handler,
1583 "Normal", I(0), "Application", I(1), NULL);
1584 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1585 HELPCTX(keyboard_appkeypad),
1586 numeric_keypad_handler, P(NULL),
1587 "Normal", I(0), "Application", I(1), "NetHack", I(2),
1591 * The Terminal/Bell panel.
1593 ctrl_settitle(b, "Terminal/Bell",
1594 "Options controlling the terminal bell");
1596 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1597 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1598 HELPCTX(bell_style),
1599 conf_radiobutton_handler, I(CONF_beep),
1600 "None (bell disabled)", I(BELL_DISABLED),
1601 "Make default system alert sound", I(BELL_DEFAULT),
1602 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1604 s = ctrl_getset(b, "Terminal/Bell", "overload",
1605 "Control the bell overload behaviour");
1606 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1607 HELPCTX(bell_overload),
1608 conf_checkbox_handler, I(CONF_bellovl));
1609 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1610 HELPCTX(bell_overload),
1611 conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1612 ctrl_editbox(s, "... in this many seconds", 't', 20,
1613 HELPCTX(bell_overload),
1614 conf_editbox_handler, I(CONF_bellovl_t),
1616 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1617 HELPCTX(bell_overload));
1618 ctrl_editbox(s, "Seconds of silence required", 's', 20,
1619 HELPCTX(bell_overload),
1620 conf_editbox_handler, I(CONF_bellovl_s),
1624 * The Terminal/Features panel.
1626 ctrl_settitle(b, "Terminal/Features",
1627 "Enabling and disabling advanced terminal features");
1629 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1630 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1631 HELPCTX(features_application),
1632 conf_checkbox_handler, I(CONF_no_applic_c));
1633 ctrl_checkbox(s, "Disable application keypad mode", 'k',
1634 HELPCTX(features_application),
1635 conf_checkbox_handler, I(CONF_no_applic_k));
1636 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1637 HELPCTX(features_mouse),
1638 conf_checkbox_handler, I(CONF_no_mouse_rep));
1639 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1640 HELPCTX(features_resize),
1641 conf_checkbox_handler,
1642 I(CONF_no_remote_resize));
1643 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1644 HELPCTX(features_altscreen),
1645 conf_checkbox_handler, I(CONF_no_alt_screen));
1646 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1647 HELPCTX(features_retitle),
1648 conf_checkbox_handler,
1649 I(CONF_no_remote_wintitle));
1650 ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1651 HELPCTX(features_qtitle),
1652 conf_radiobutton_handler,
1653 I(CONF_remote_qtitle_action),
1654 "None", I(TITLE_NONE),
1655 "Empty string", I(TITLE_EMPTY),
1656 "Window title", I(TITLE_REAL), NULL);
1657 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1658 HELPCTX(features_dbackspace),
1659 conf_checkbox_handler, I(CONF_no_dbackspace));
1660 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1661 'r', HELPCTX(features_charset), conf_checkbox_handler,
1662 I(CONF_no_remote_charset));
1663 ctrl_checkbox(s, "Disable Arabic text shaping",
1664 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1665 I(CONF_arabicshaping));
1666 ctrl_checkbox(s, "Disable bidirectional text display",
1667 'd', HELPCTX(features_bidi), conf_checkbox_handler,
1673 str = dupprintf("Options controlling %s's window", appname);
1674 ctrl_settitle(b, "Window", str);
1677 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1678 ctrl_columns(s, 2, 50, 50);
1679 c = ctrl_editbox(s, "Columns", 'm', 100,
1680 HELPCTX(window_size),
1681 conf_editbox_handler, I(CONF_width), I(-1));
1682 c->generic.column = 0;
1683 c = ctrl_editbox(s, "Rows", 'r', 100,
1684 HELPCTX(window_size),
1685 conf_editbox_handler, I(CONF_height),I(-1));
1686 c->generic.column = 1;
1687 ctrl_columns(s, 1, 100);
1689 s = ctrl_getset(b, "Window", "scrollback",
1690 "Control the scrollback in the window");
1691 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1692 HELPCTX(window_scrollback),
1693 conf_editbox_handler, I(CONF_savelines), I(-1));
1694 ctrl_checkbox(s, "Display scrollbar", 'd',
1695 HELPCTX(window_scrollback),
1696 conf_checkbox_handler, I(CONF_scrollbar));
1697 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1698 HELPCTX(window_scrollback),
1699 conf_checkbox_handler, I(CONF_scroll_on_key));
1700 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1701 HELPCTX(window_scrollback),
1702 conf_checkbox_handler, I(CONF_scroll_on_disp));
1703 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1704 HELPCTX(window_erased),
1705 conf_checkbox_handler,
1706 I(CONF_erase_to_scrollback));
1709 * The Window/Appearance panel.
1711 str = dupprintf("Configure the appearance of %s's window", appname);
1712 ctrl_settitle(b, "Window/Appearance", str);
1715 s = ctrl_getset(b, "Window/Appearance", "cursor",
1716 "Adjust the use of the cursor");
1717 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1718 HELPCTX(appearance_cursor),
1719 conf_radiobutton_handler,
1720 I(CONF_cursor_type),
1722 "Underline", 'u', I(1),
1723 "Vertical line", 'v', I(2), NULL);
1724 ctrl_checkbox(s, "Cursor blinks", 'b',
1725 HELPCTX(appearance_cursor),
1726 conf_checkbox_handler, I(CONF_blink_cur));
1728 s = ctrl_getset(b, "Window/Appearance", "font",
1730 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1731 HELPCTX(appearance_font),
1732 conf_fontsel_handler, I(CONF_font));
1734 s = ctrl_getset(b, "Window/Appearance", "mouse",
1735 "Adjust the use of the mouse pointer");
1736 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1737 HELPCTX(appearance_hidemouse),
1738 conf_checkbox_handler, I(CONF_hide_mouseptr));
1740 s = ctrl_getset(b, "Window/Appearance", "border",
1741 "Adjust the window border");
1742 ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1743 HELPCTX(appearance_border),
1744 conf_editbox_handler,
1745 I(CONF_window_border), I(-1));
1748 * The Window/Behaviour panel.
1750 str = dupprintf("Configure the behaviour of %s's window", appname);
1751 ctrl_settitle(b, "Window/Behaviour", str);
1754 s = ctrl_getset(b, "Window/Behaviour", "title",
1755 "Adjust the behaviour of the window title");
1756 ctrl_editbox(s, "Window title:", 't', 100,
1757 HELPCTX(appearance_title),
1758 conf_editbox_handler, I(CONF_wintitle), I(1));
1759 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1760 HELPCTX(appearance_title),
1761 conf_checkbox_handler,
1762 I(CHECKBOX_INVERT | CONF_win_name_always));
1764 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1765 ctrl_checkbox(s, "Warn before closing window", 'w',
1766 HELPCTX(behaviour_closewarn),
1767 conf_checkbox_handler, I(CONF_warn_on_close));
1770 * The Window/Translation panel.
1772 ctrl_settitle(b, "Window/Translation",
1773 "Options controlling character set translation");
1775 s = ctrl_getset(b, "Window/Translation", "trans",
1776 "Character set translation");
1777 ctrl_combobox(s, "Remote character set:",
1778 'r', 100, HELPCTX(translation_codepage),
1779 codepage_handler, P(NULL), P(NULL));
1781 s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1782 ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1783 HELPCTX(translation_cjk_ambig_wide),
1784 conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1786 str = dupprintf("Adjust how %s handles line drawing characters", appname);
1787 s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1789 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1790 HELPCTX(translation_linedraw),
1791 conf_radiobutton_handler,
1793 "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1794 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1796 ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1797 HELPCTX(selection_linedraw),
1798 conf_checkbox_handler, I(CONF_rawcnp));
1801 * The Window/Selection panel.
1803 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1805 s = ctrl_getset(b, "Window/Selection", "mouse",
1806 "Control use of mouse");
1807 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1808 HELPCTX(selection_shiftdrag),
1809 conf_checkbox_handler, I(CONF_mouse_override));
1810 ctrl_radiobuttons(s,
1811 "Default selection mode (Alt+drag does the other one):",
1813 HELPCTX(selection_rect),
1814 conf_radiobutton_handler,
1815 I(CONF_rect_select),
1816 "Normal", 'n', I(0),
1817 "Rectangular block", 'r', I(1), NULL);
1819 s = ctrl_getset(b, "Window/Selection", "charclass",
1820 "Control the select-one-word-at-a-time mode");
1821 ccd = (struct charclass_data *)
1822 ctrl_alloc(b, sizeof(struct charclass_data));
1823 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1824 HELPCTX(selection_charclasses),
1825 charclass_handler, P(ccd));
1826 ccd->listbox->listbox.multisel = 1;
1827 ccd->listbox->listbox.ncols = 4;
1828 ccd->listbox->listbox.percentages = snewn(4, int);
1829 ccd->listbox->listbox.percentages[0] = 15;
1830 ccd->listbox->listbox.percentages[1] = 25;
1831 ccd->listbox->listbox.percentages[2] = 20;
1832 ccd->listbox->listbox.percentages[3] = 40;
1833 ctrl_columns(s, 2, 67, 33);
1834 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1835 HELPCTX(selection_charclasses),
1836 charclass_handler, P(ccd), P(NULL));
1837 ccd->editbox->generic.column = 0;
1838 ccd->button = ctrl_pushbutton(s, "Set", 's',
1839 HELPCTX(selection_charclasses),
1840 charclass_handler, P(ccd));
1841 ccd->button->generic.column = 1;
1842 ctrl_columns(s, 1, 100);
1845 * The Window/Colours panel.
1847 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1849 s = ctrl_getset(b, "Window/Colours", "general",
1850 "General options for colour usage");
1851 ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1852 HELPCTX(colours_ansi),
1853 conf_checkbox_handler, I(CONF_ansi_colour));
1854 ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1855 HELPCTX(colours_xterm256), conf_checkbox_handler,
1856 I(CONF_xterm_256_colour));
1857 ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
1858 HELPCTX(colours_bold),
1859 conf_radiobutton_handler, I(CONF_bold_style),
1865 str = dupprintf("Adjust the precise colours %s displays", appname);
1866 s = ctrl_getset(b, "Window/Colours", "adjust", str);
1868 ctrl_text(s, "Select a colour from the list, and then click the"
1869 " Modify button to change its appearance.",
1870 HELPCTX(colours_config));
1871 ctrl_columns(s, 2, 67, 33);
1872 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1873 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1874 HELPCTX(colours_config), colour_handler, P(cd));
1875 cd->listbox->generic.column = 0;
1876 cd->listbox->listbox.height = 7;
1877 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1878 c->generic.column = 1;
1879 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1880 colour_handler, P(cd), P(NULL));
1881 cd->redit->generic.column = 1;
1882 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1883 colour_handler, P(cd), P(NULL));
1884 cd->gedit->generic.column = 1;
1885 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1886 colour_handler, P(cd), P(NULL));
1887 cd->bedit->generic.column = 1;
1888 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1889 colour_handler, P(cd));
1890 cd->button->generic.column = 1;
1891 ctrl_columns(s, 1, 100);
1894 * The Connection panel. This doesn't show up if we're in a
1895 * non-network utility such as pterm. We tell this by being
1896 * passed a protocol < 0.
1898 if (protocol >= 0) {
1899 ctrl_settitle(b, "Connection", "Options controlling the connection");
1901 s = ctrl_getset(b, "Connection", "keepalive",
1902 "Sending of null packets to keep session active");
1903 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1904 HELPCTX(connection_keepalive),
1905 conf_editbox_handler, I(CONF_ping_interval),
1909 s = ctrl_getset(b, "Connection", "tcp",
1910 "Low-level TCP connection options");
1911 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1912 'n', HELPCTX(connection_nodelay),
1913 conf_checkbox_handler,
1914 I(CONF_tcp_nodelay));
1915 ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1916 'p', HELPCTX(connection_tcpkeepalive),
1917 conf_checkbox_handler,
1918 I(CONF_tcp_keepalives));
1920 s = ctrl_getset(b, "Connection", "ipversion",
1921 "Internet protocol version");
1922 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1923 HELPCTX(connection_ipversion),
1924 conf_radiobutton_handler,
1925 I(CONF_addressfamily),
1926 "Auto", 'u', I(ADDRTYPE_UNSPEC),
1927 "IPv4", '4', I(ADDRTYPE_IPV4),
1928 "IPv6", '6', I(ADDRTYPE_IPV6),
1933 const char *label = backend_from_proto(PROT_SSH) ?
1934 "Logical name of remote host (e.g. for SSH key lookup):" :
1935 "Logical name of remote host:";
1936 s = ctrl_getset(b, "Connection", "identity",
1937 "Logical name of remote host");
1938 ctrl_editbox(s, label, 'm', 100,
1939 HELPCTX(connection_loghost),
1940 conf_editbox_handler, I(CONF_loghost), I(1));
1945 * A sub-panel Connection/Data, containing options that
1946 * decide on data to send to the server.
1949 ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1951 s = ctrl_getset(b, "Connection/Data", "login",
1953 ctrl_editbox(s, "Auto-login username", 'u', 50,
1954 HELPCTX(connection_username),
1955 conf_editbox_handler, I(CONF_username), I(1));
1957 /* We assume the local username is sufficiently stable
1958 * to include on the dialog box. */
1959 char *user = get_username();
1960 char *userlabel = dupprintf("Use system username (%s)",
1963 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1964 HELPCTX(connection_username_from_env),
1965 conf_radiobutton_handler,
1966 I(CONF_username_from_env),
1973 s = ctrl_getset(b, "Connection/Data", "term",
1974 "Terminal details");
1975 ctrl_editbox(s, "Terminal-type string", 't', 50,
1976 HELPCTX(connection_termtype),
1977 conf_editbox_handler, I(CONF_termtype), I(1));
1978 ctrl_editbox(s, "Terminal speeds", 's', 50,
1979 HELPCTX(connection_termspeed),
1980 conf_editbox_handler, I(CONF_termspeed), I(1));
1982 s = ctrl_getset(b, "Connection/Data", "env",
1983 "Environment variables");
1984 ctrl_columns(s, 2, 80, 20);
1985 ed = (struct environ_data *)
1986 ctrl_alloc(b, sizeof(struct environ_data));
1987 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1988 HELPCTX(telnet_environ),
1989 environ_handler, P(ed), P(NULL));
1990 ed->varbox->generic.column = 0;
1991 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1992 HELPCTX(telnet_environ),
1993 environ_handler, P(ed), P(NULL));
1994 ed->valbox->generic.column = 0;
1995 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1996 HELPCTX(telnet_environ),
1997 environ_handler, P(ed));
1998 ed->addbutton->generic.column = 1;
1999 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2000 HELPCTX(telnet_environ),
2001 environ_handler, P(ed));
2002 ed->rembutton->generic.column = 1;
2003 ctrl_columns(s, 1, 100);
2004 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2005 HELPCTX(telnet_environ),
2006 environ_handler, P(ed));
2007 ed->listbox->listbox.height = 3;
2008 ed->listbox->listbox.ncols = 2;
2009 ed->listbox->listbox.percentages = snewn(2, int);
2010 ed->listbox->listbox.percentages[0] = 30;
2011 ed->listbox->listbox.percentages[1] = 70;
2018 * The Connection/Proxy panel.
2020 ctrl_settitle(b, "Connection/Proxy",
2021 "Options controlling proxy usage");
2023 s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
2024 ctrl_radiobuttons(s, "Proxy type:", 't', 3,
2025 HELPCTX(proxy_type),
2026 conf_radiobutton_handler,
2028 "None", I(PROXY_NONE),
2029 "SOCKS 4", I(PROXY_SOCKS4),
2030 "SOCKS 5", I(PROXY_SOCKS5),
2031 "HTTP", I(PROXY_HTTP),
2032 "Telnet", I(PROXY_TELNET),
2034 ctrl_columns(s, 2, 80, 20);
2035 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
2036 HELPCTX(proxy_main),
2037 conf_editbox_handler,
2038 I(CONF_proxy_host), I(1));
2039 c->generic.column = 0;
2040 c = ctrl_editbox(s, "Port", 'p', 100,
2041 HELPCTX(proxy_main),
2042 conf_editbox_handler,
2045 c->generic.column = 1;
2046 ctrl_columns(s, 1, 100);
2047 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
2048 HELPCTX(proxy_exclude),
2049 conf_editbox_handler,
2050 I(CONF_proxy_exclude_list), I(1));
2051 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
2052 HELPCTX(proxy_exclude),
2053 conf_checkbox_handler,
2054 I(CONF_even_proxy_localhost));
2055 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
2057 conf_radiobutton_handler,
2061 "Yes", I(FORCE_ON), NULL);
2062 ctrl_editbox(s, "Username", 'u', 60,
2063 HELPCTX(proxy_auth),
2064 conf_editbox_handler,
2065 I(CONF_proxy_username), I(1));
2066 c = ctrl_editbox(s, "Password", 'w', 60,
2067 HELPCTX(proxy_auth),
2068 conf_editbox_handler,
2069 I(CONF_proxy_password), I(1));
2070 c->editbox.password = 1;
2071 ctrl_editbox(s, "Telnet command", 'm', 100,
2072 HELPCTX(proxy_command),
2073 conf_editbox_handler,
2074 I(CONF_proxy_telnet_command), I(1));
2076 ctrl_radiobuttons(s, "Print proxy diagnostics "
2077 "in the terminal window", 'r', 5,
2078 HELPCTX(proxy_main),
2079 conf_radiobutton_handler,
2080 I(CONF_proxy_log_to_term),
2083 "Only until session starts", I(AUTO), NULL);
2087 * The Telnet panel exists in the base config box, and in a
2088 * mid-session reconfig box _if_ we're using Telnet.
2090 if (!midsession || protocol == PROT_TELNET) {
2092 * The Connection/Telnet panel.
2094 ctrl_settitle(b, "Connection/Telnet",
2095 "Options controlling Telnet connections");
2097 s = ctrl_getset(b, "Connection/Telnet", "protocol",
2098 "Telnet protocol adjustments");
2101 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2103 HELPCTX(telnet_oldenviron),
2104 conf_radiobutton_handler,
2105 I(CONF_rfc_environ),
2106 "BSD (commonplace)", 'b', I(0),
2107 "RFC 1408 (unusual)", 'f', I(1), NULL);
2108 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2109 HELPCTX(telnet_passive),
2110 conf_radiobutton_handler,
2111 I(CONF_passive_telnet),
2112 "Passive", I(1), "Active", I(0), NULL);
2114 ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2115 HELPCTX(telnet_specialkeys),
2116 conf_checkbox_handler,
2117 I(CONF_telnet_keyboard));
2118 ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2119 'm', HELPCTX(telnet_newline),
2120 conf_checkbox_handler,
2121 I(CONF_telnet_newline));
2127 * The Connection/Rlogin panel.
2129 ctrl_settitle(b, "Connection/Rlogin",
2130 "Options controlling Rlogin connections");
2132 s = ctrl_getset(b, "Connection/Rlogin", "data",
2133 "Data to send to the server");
2134 ctrl_editbox(s, "Local username:", 'l', 50,
2135 HELPCTX(rlogin_localuser),
2136 conf_editbox_handler, I(CONF_localusername), I(1));
2141 * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2142 * when we're not doing SSH.
2145 if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2148 * The Connection/SSH panel.
2150 ctrl_settitle(b, "Connection/SSH",
2151 "Options controlling SSH connections");
2153 /* SSH-1 or connection-sharing downstream */
2154 if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
2155 s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2156 ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2157 "session; it is only here so that sub-panels of it can "
2158 "exist without looking strange.", HELPCTX(no_help));
2163 s = ctrl_getset(b, "Connection/SSH", "data",
2164 "Data to send to the server");
2165 ctrl_editbox(s, "Remote command:", 'r', 100,
2166 HELPCTX(ssh_command),
2167 conf_editbox_handler, I(CONF_remote_cmd), I(1));
2169 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2170 ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2171 HELPCTX(ssh_noshell),
2172 conf_checkbox_handler,
2173 I(CONF_ssh_no_shell));
2176 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2177 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2179 ctrl_checkbox(s, "Enable compression", 'e',
2180 HELPCTX(ssh_compress),
2181 conf_checkbox_handler,
2182 I(CONF_compression));
2186 s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
2188 ctrl_checkbox(s, "Share SSH connections if possible", 's',
2190 conf_checkbox_handler,
2191 I(CONF_ssh_connection_sharing));
2193 ctrl_text(s, "Permitted roles in a shared connection:",
2194 HELPCTX(ssh_share));
2195 ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
2197 conf_checkbox_handler,
2198 I(CONF_ssh_connection_sharing_upstream));
2199 ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
2201 conf_checkbox_handler,
2202 I(CONF_ssh_connection_sharing_downstream));
2206 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2208 ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2209 HELPCTX(ssh_protocol),
2210 conf_radiobutton_handler,
2212 "1 only", 'l', I(0),
2215 "2 only", 'y', I(3), NULL);
2219 * The Connection/SSH/Kex panel. (Owing to repeat key
2220 * exchange, much of this is meaningful in mid-session _if_
2221 * we're using SSH-2 and are not a connection-sharing
2222 * downstream, or haven't decided yet.)
2224 if (protcfginfo != 1 && protcfginfo != -1) {
2225 ctrl_settitle(b, "Connection/SSH/Kex",
2226 "Options controlling SSH key exchange");
2228 s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2229 "Key exchange algorithm options");
2230 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2231 HELPCTX(ssh_kexlist),
2232 kexlist_handler, P(NULL));
2233 c->listbox.height = 5;
2235 s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2236 "Options controlling key re-exchange");
2238 ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2239 HELPCTX(ssh_kex_repeat),
2240 conf_editbox_handler,
2241 I(CONF_ssh_rekey_time),
2243 ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2244 HELPCTX(ssh_kex_repeat),
2245 conf_editbox_handler,
2246 I(CONF_ssh_rekey_data),
2248 ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2249 HELPCTX(ssh_kex_repeat));
2253 * Manual host key configuration is irrelevant mid-session,
2254 * as we enforce that the host key for rekeys is the
2255 * same as that used at the start of the session.
2258 s = ctrl_getset(b, "Connection/SSH/Kex", "hostkeys",
2259 "Manually configure host keys for this connection");
2261 ctrl_columns(s, 2, 75, 25);
2262 c = ctrl_text(s, "Host keys or fingerprints to accept:",
2263 HELPCTX(ssh_kex_manual_hostkeys));
2264 c->generic.column = 0;
2265 /* You want to select from the list, _then_ hit Remove. So
2266 * tab order should be that way round. */
2267 mh = (struct manual_hostkey_data *)
2268 ctrl_alloc(b,sizeof(struct manual_hostkey_data));
2269 mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2270 HELPCTX(ssh_kex_manual_hostkeys),
2271 manual_hostkey_handler, P(mh));
2272 mh->rembutton->generic.column = 1;
2273 mh->rembutton->generic.tabdelay = 1;
2274 mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2275 HELPCTX(ssh_kex_manual_hostkeys),
2276 manual_hostkey_handler, P(mh));
2277 /* This list box can't be very tall, because there's not
2278 * much room in the pane on Windows at least. This makes
2279 * it become really unhelpful if a horizontal scrollbar
2280 * appears, so we suppress that. */
2281 mh->listbox->listbox.height = 2;
2282 mh->listbox->listbox.hscroll = FALSE;
2283 ctrl_tabdelay(s, mh->rembutton);
2284 mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
2285 HELPCTX(ssh_kex_manual_hostkeys),
2286 manual_hostkey_handler, P(mh), P(NULL));
2287 mh->keybox->generic.column = 0;
2288 mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
2289 HELPCTX(ssh_kex_manual_hostkeys),
2290 manual_hostkey_handler, P(mh));
2291 mh->addbutton->generic.column = 1;
2292 ctrl_columns(s, 1, 100);
2295 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2297 * The Connection/SSH/Cipher panel.
2299 ctrl_settitle(b, "Connection/SSH/Cipher",
2300 "Options controlling SSH encryption");
2302 s = ctrl_getset(b, "Connection/SSH/Cipher",
2303 "encryption", "Encryption options");
2304 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2305 HELPCTX(ssh_ciphers),
2306 cipherlist_handler, P(NULL));
2307 c->listbox.height = 6;
2309 ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2310 HELPCTX(ssh_ciphers),
2311 conf_checkbox_handler,
2312 I(CONF_ssh2_des_cbc));
2318 * The Connection/SSH/Auth panel.
2320 ctrl_settitle(b, "Connection/SSH/Auth",
2321 "Options controlling SSH authentication");
2323 s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2324 ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2325 'd', HELPCTX(ssh_auth_banner),
2326 conf_checkbox_handler,
2327 I(CONF_ssh_show_banner));
2328 ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2329 HELPCTX(ssh_auth_bypass),
2330 conf_checkbox_handler,
2331 I(CONF_ssh_no_userauth));
2333 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2334 "Authentication methods");
2335 ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2336 HELPCTX(ssh_auth_pageant),
2337 conf_checkbox_handler,
2339 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2340 HELPCTX(ssh_auth_tis),
2341 conf_checkbox_handler,
2342 I(CONF_try_tis_auth));
2343 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2344 'i', HELPCTX(ssh_auth_ki),
2345 conf_checkbox_handler,
2346 I(CONF_try_ki_auth));
2348 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2349 "Authentication parameters");
2350 ctrl_checkbox(s, "Allow agent forwarding", 'f',
2351 HELPCTX(ssh_auth_agentfwd),
2352 conf_checkbox_handler, I(CONF_agentfwd));
2353 ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2354 HELPCTX(ssh_auth_changeuser),
2355 conf_checkbox_handler,
2356 I(CONF_change_username));
2357 ctrl_filesel(s, "Private key file for authentication:", 'k',
2358 FILTER_KEY_FILES, FALSE, "Select private key file",
2359 HELPCTX(ssh_auth_privkey),
2360 conf_filesel_handler, I(CONF_keyfile));
2364 * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2365 * the main Auth panel.
2367 ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2368 "Options controlling GSSAPI authentication");
2369 s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2371 ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2372 't', HELPCTX(ssh_gssapi),
2373 conf_checkbox_handler,
2374 I(CONF_try_gssapi_auth));
2376 ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2377 HELPCTX(ssh_gssapi_delegation),
2378 conf_checkbox_handler,
2382 * GSSAPI library selection.
2385 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2386 'p', HELPCTX(ssh_gssapi_libraries),
2387 gsslist_handler, P(NULL));
2388 c->listbox.height = ngsslibs;
2391 * I currently assume that if more than one GSS
2392 * library option is available, then one of them is
2393 * 'user-supplied' and so we should present the
2394 * following file selector. This is at least half-
2395 * reasonable, because if we're using statically
2396 * linked GSSAPI then there will only be one option
2397 * and no way to load from a user-supplied library,
2398 * whereas if we're using dynamic libraries then
2399 * there will almost certainly be some default
2400 * option in addition to a user-supplied path. If
2401 * anyone ever ports PuTTY to a system on which
2402 * dynamic-library GSSAPI is available but there is
2403 * absolutely no consensus on where to keep the
2404 * libraries, there'll need to be a flag alongside
2405 * ngsslibs to control whether the file selector is
2409 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2410 FILTER_DYNLIB_FILES, FALSE, "Select library file",
2411 HELPCTX(ssh_gssapi_libraries),
2412 conf_filesel_handler,
2413 I(CONF_ssh_gss_custom));
2420 * The Connection/SSH/TTY panel.
2422 ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2424 s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2425 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2427 conf_checkbox_handler,
2430 s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2432 td = (struct ttymodes_data *)
2433 ctrl_alloc(b, sizeof(struct ttymodes_data));
2434 ctrl_columns(s, 2, 75, 25);
2435 c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2436 c->generic.column = 0;
2437 td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2438 HELPCTX(ssh_ttymodes),
2439 ttymodes_handler, P(td));
2440 td->rembutton->generic.column = 1;
2441 td->rembutton->generic.tabdelay = 1;
2442 ctrl_columns(s, 1, 100);
2443 td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2444 HELPCTX(ssh_ttymodes),
2445 ttymodes_handler, P(td));
2446 td->listbox->listbox.multisel = 1;
2447 td->listbox->listbox.height = 4;
2448 td->listbox->listbox.ncols = 2;
2449 td->listbox->listbox.percentages = snewn(2, int);
2450 td->listbox->listbox.percentages[0] = 40;
2451 td->listbox->listbox.percentages[1] = 60;
2452 ctrl_tabdelay(s, td->rembutton);
2453 ctrl_columns(s, 2, 75, 25);
2454 td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2455 HELPCTX(ssh_ttymodes),
2456 ttymodes_handler, P(td));
2457 td->modelist->generic.column = 0;
2458 td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2459 HELPCTX(ssh_ttymodes),
2460 ttymodes_handler, P(td));
2461 td->addbutton->generic.column = 1;
2462 td->addbutton->generic.tabdelay = 1;
2463 ctrl_columns(s, 1, 100); /* column break */
2464 /* Bit of a hack to get the value radio buttons and
2465 * edit-box on the same row. */
2466 ctrl_columns(s, 3, 25, 50, 25);
2467 c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2468 c->generic.column = 0;
2469 td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2470 HELPCTX(ssh_ttymodes),
2471 ttymodes_handler, P(td),
2472 "Auto", NO_SHORTCUT, P(NULL),
2473 "This:", NO_SHORTCUT, P(NULL),
2475 td->valradio->generic.column = 1;
2476 td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2477 HELPCTX(ssh_ttymodes),
2478 ttymodes_handler, P(td), P(NULL));
2479 td->valbox->generic.column = 2;
2480 ctrl_tabdelay(s, td->addbutton);
2486 * The Connection/SSH/X11 panel.
2488 ctrl_settitle(b, "Connection/SSH/X11",
2489 "Options controlling SSH X11 forwarding");
2491 s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2492 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2493 HELPCTX(ssh_tunnels_x11),
2494 conf_checkbox_handler,I(CONF_x11_forward));
2495 ctrl_editbox(s, "X display location", 'x', 50,
2496 HELPCTX(ssh_tunnels_x11),
2497 conf_editbox_handler, I(CONF_x11_display), I(1));
2498 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2499 HELPCTX(ssh_tunnels_x11auth),
2500 conf_radiobutton_handler,
2502 "MIT-Magic-Cookie-1", I(X11_MIT),
2503 "XDM-Authorization-1", I(X11_XDM), NULL);
2507 * The Tunnels panel _is_ still available in mid-session.
2509 ctrl_settitle(b, "Connection/SSH/Tunnels",
2510 "Options controlling SSH port forwarding");
2512 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2514 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2515 HELPCTX(ssh_tunnels_portfwd_localhost),
2516 conf_checkbox_handler,
2517 I(CONF_lport_acceptall));
2518 ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2519 HELPCTX(ssh_tunnels_portfwd_localhost),
2520 conf_checkbox_handler,
2521 I(CONF_rport_acceptall));
2523 ctrl_columns(s, 3, 55, 20, 25);
2524 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2525 c->generic.column = COLUMN_FIELD(0,2);
2526 /* You want to select from the list, _then_ hit Remove. So tab order
2527 * should be that way round. */
2528 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2529 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2530 HELPCTX(ssh_tunnels_portfwd),
2531 portfwd_handler, P(pfd));
2532 pfd->rembutton->generic.column = 2;
2533 pfd->rembutton->generic.tabdelay = 1;
2534 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2535 HELPCTX(ssh_tunnels_portfwd),
2536 portfwd_handler, P(pfd));
2537 pfd->listbox->listbox.height = 3;
2538 pfd->listbox->listbox.ncols = 2;
2539 pfd->listbox->listbox.percentages = snewn(2, int);
2540 pfd->listbox->listbox.percentages[0] = 20;
2541 pfd->listbox->listbox.percentages[1] = 80;
2542 ctrl_tabdelay(s, pfd->rembutton);
2543 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2544 /* You want to enter source, destination and type, _then_ hit Add.
2545 * Again, we adjust the tab order to reflect this. */
2546 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2547 HELPCTX(ssh_tunnels_portfwd),
2548 portfwd_handler, P(pfd));
2549 pfd->addbutton->generic.column = 2;
2550 pfd->addbutton->generic.tabdelay = 1;
2551 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2552 HELPCTX(ssh_tunnels_portfwd),
2553 portfwd_handler, P(pfd), P(NULL));
2554 pfd->sourcebox->generic.column = 0;
2555 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2556 HELPCTX(ssh_tunnels_portfwd),
2557 portfwd_handler, P(pfd), P(NULL));
2558 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2559 HELPCTX(ssh_tunnels_portfwd),
2560 portfwd_handler, P(pfd),
2561 "Local", 'l', P(NULL),
2562 "Remote", 'm', P(NULL),
2563 "Dynamic", 'y', P(NULL),
2566 pfd->addressfamily =
2567 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2568 HELPCTX(ssh_tunnels_portfwd_ipversion),
2569 portfwd_handler, P(pfd),
2570 "Auto", 'u', I(ADDRTYPE_UNSPEC),
2571 "IPv4", '4', I(ADDRTYPE_IPV4),
2572 "IPv6", '6', I(ADDRTYPE_IPV6),
2575 ctrl_tabdelay(s, pfd->addbutton);
2576 ctrl_columns(s, 1, 100);
2580 * The Connection/SSH/Bugs panels.
2582 ctrl_settitle(b, "Connection/SSH/Bugs",
2583 "Workarounds for SSH server bugs");
2585 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2586 "Detection of known bugs in SSH servers");
2587 ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2588 HELPCTX(ssh_bugs_ignore1),
2589 sshbug_handler, I(CONF_sshbug_ignore1));
2590 ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2591 HELPCTX(ssh_bugs_plainpw1),
2592 sshbug_handler, I(CONF_sshbug_plainpw1));
2593 ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2594 HELPCTX(ssh_bugs_rsa1),
2595 sshbug_handler, I(CONF_sshbug_rsa1));
2596 ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2597 HELPCTX(ssh_bugs_ignore2),
2598 sshbug_handler, I(CONF_sshbug_ignore2));
2599 ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
2600 20, HELPCTX(ssh_bugs_winadj),
2601 sshbug_handler, I(CONF_sshbug_winadj));
2602 ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2603 HELPCTX(ssh_bugs_hmac2),
2604 sshbug_handler, I(CONF_sshbug_hmac2));
2605 ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2606 HELPCTX(ssh_bugs_derivekey2),
2607 sshbug_handler, I(CONF_sshbug_derivekey2));
2609 ctrl_settitle(b, "Connection/SSH/More bugs",
2610 "Further workarounds for SSH server bugs");
2612 s = ctrl_getset(b, "Connection/SSH/More bugs", "main",
2613 "Detection of known bugs in SSH servers");
2614 ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2615 HELPCTX(ssh_bugs_rsapad2),
2616 sshbug_handler, I(CONF_sshbug_rsapad2));
2617 ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2618 HELPCTX(ssh_bugs_pksessid2),
2619 sshbug_handler, I(CONF_sshbug_pksessid2));
2620 ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2621 HELPCTX(ssh_bugs_rekey2),
2622 sshbug_handler, I(CONF_sshbug_rekey2));
2623 ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2624 HELPCTX(ssh_bugs_maxpkt2),
2625 sshbug_handler, I(CONF_sshbug_maxpkt2));
2626 ctrl_droplist(s, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20,
2627 HELPCTX(ssh_bugs_oldgex2),
2628 sshbug_handler, I(CONF_sshbug_oldgex2));
2629 ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
2630 HELPCTX(ssh_bugs_chanreq),
2631 sshbug_handler, I(CONF_sshbug_chanreq));