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 hklist_handler(union control *ctrl, void *dlg,
470 void *data, int event)
472 Conf *conf = (Conf *)data;
473 if (event == EVENT_REFRESH) {
476 static const struct { const char *s; int k; } hks[] = {
477 { "Ed25519", HK_ED25519 },
478 { "ECDSA", HK_ECDSA },
481 { "-- warn below here --", HK_WARN }
484 /* Set up the "host key preference" box. */
485 /* (hklist assumed to contain all algorithms) */
486 dlg_update_start(ctrl, dlg);
487 dlg_listbox_clear(ctrl, dlg);
488 for (i = 0; i < HK_MAX; i++) {
489 int k = conf_get_int_int(conf, CONF_ssh_hklist, i);
491 const char *kstr = NULL;
492 for (j = 0; j < lenof(hks); j++) {
498 dlg_listbox_addwithid(ctrl, dlg, kstr, k);
500 dlg_update_done(ctrl, dlg);
502 } else if (event == EVENT_VALCHANGE) {
505 /* Update array to match the list box. */
506 for (i=0; i < HK_MAX; i++)
507 conf_set_int_int(conf, CONF_ssh_hklist, i,
508 dlg_listbox_getid(ctrl, dlg, i));
512 static void printerbox_handler(union control *ctrl, void *dlg,
513 void *data, int event)
515 Conf *conf = (Conf *)data;
516 if (event == EVENT_REFRESH) {
521 dlg_update_start(ctrl, dlg);
523 * Some backends may wish to disable the drop-down list on
524 * this edit box. Be prepared for this.
526 if (ctrl->editbox.has_list) {
527 dlg_listbox_clear(ctrl, dlg);
528 dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
529 pe = printer_start_enum(&nprinters);
530 for (i = 0; i < nprinters; i++)
531 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
532 printer_finish_enum(pe);
534 printer = conf_get_str(conf, CONF_printer);
536 printer = PRINTER_DISABLED_STRING;
537 dlg_editbox_set(ctrl, dlg, printer);
538 dlg_update_done(ctrl, dlg);
539 } else if (event == EVENT_VALCHANGE) {
540 char *printer = dlg_editbox_get(ctrl, dlg);
541 if (!strcmp(printer, PRINTER_DISABLED_STRING))
543 conf_set_str(conf, CONF_printer, printer);
548 static void codepage_handler(union control *ctrl, void *dlg,
549 void *data, int event)
551 Conf *conf = (Conf *)data;
552 if (event == EVENT_REFRESH) {
554 const char *cp, *thiscp;
555 dlg_update_start(ctrl, dlg);
556 thiscp = cp_name(decode_codepage(conf_get_str(conf,
557 CONF_line_codepage)));
558 dlg_listbox_clear(ctrl, dlg);
559 for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
560 dlg_listbox_add(ctrl, dlg, cp);
561 dlg_editbox_set(ctrl, dlg, thiscp);
562 conf_set_str(conf, CONF_line_codepage, thiscp);
563 dlg_update_done(ctrl, dlg);
564 } else if (event == EVENT_VALCHANGE) {
565 char *codepage = dlg_editbox_get(ctrl, dlg);
566 conf_set_str(conf, CONF_line_codepage,
567 cp_name(decode_codepage(codepage)));
572 static void sshbug_handler(union control *ctrl, void *dlg,
573 void *data, int event)
575 Conf *conf = (Conf *)data;
576 if (event == EVENT_REFRESH) {
578 * We must fetch the previously configured value from the Conf
579 * before we start modifying the drop-down list, otherwise the
580 * spurious SELCHANGE we trigger in the process will overwrite
581 * the value we wanted to keep.
583 int oldconf = conf_get_int(conf, ctrl->listbox.context.i);
584 dlg_update_start(ctrl, dlg);
585 dlg_listbox_clear(ctrl, dlg);
586 dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
587 dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
588 dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
590 case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
591 case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
592 case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
594 dlg_update_done(ctrl, dlg);
595 } else if (event == EVENT_SELCHANGE) {
596 int i = dlg_listbox_index(ctrl, dlg);
600 i = dlg_listbox_getid(ctrl, dlg, i);
601 conf_set_int(conf, ctrl->listbox.context.i, i);
605 struct sessionsaver_data {
606 union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
607 union control *okbutton, *cancelbutton;
608 struct sesslist sesslist;
610 char *savedsession; /* the current contents of ssd->editbox */
613 static void sessionsaver_data_free(void *ssdv)
615 struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
616 get_sesslist(&ssd->sesslist, FALSE);
617 sfree(ssd->savedsession);
622 * Helper function to load the session selected in the list box, if
623 * any, as this is done in more than one place below. Returns 0 for
626 static int load_selected_session(struct sessionsaver_data *ssd,
627 void *dlg, Conf *conf, int *maybe_launch)
629 int i = dlg_listbox_index(ssd->listbox, dlg);
635 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
636 load_settings(ssd->sesslist.sessions[i], conf);
637 sfree(ssd->savedsession);
638 ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]);
640 *maybe_launch = !isdef;
641 dlg_refresh(NULL, dlg);
642 /* Restore the selection, which might have been clobbered by
643 * changing the value of the edit box. */
644 dlg_listbox_select(ssd->listbox, dlg, i);
648 static void sessionsaver_handler(union control *ctrl, void *dlg,
649 void *data, int event)
651 Conf *conf = (Conf *)data;
652 struct sessionsaver_data *ssd =
653 (struct sessionsaver_data *)ctrl->generic.context.p;
655 if (event == EVENT_REFRESH) {
656 if (ctrl == ssd->editbox) {
657 dlg_editbox_set(ctrl, dlg, ssd->savedsession);
658 } else if (ctrl == ssd->listbox) {
660 dlg_update_start(ctrl, dlg);
661 dlg_listbox_clear(ctrl, dlg);
662 for (i = 0; i < ssd->sesslist.nsessions; i++)
663 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
664 dlg_update_done(ctrl, dlg);
666 } else if (event == EVENT_VALCHANGE) {
667 int top, bottom, halfway, i;
668 if (ctrl == ssd->editbox) {
669 sfree(ssd->savedsession);
670 ssd->savedsession = dlg_editbox_get(ctrl, dlg);
671 top = ssd->sesslist.nsessions;
673 while (top-bottom > 1) {
674 halfway = (top+bottom)/2;
675 i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]);
682 if (top == ssd->sesslist.nsessions) {
685 dlg_listbox_select(ssd->listbox, dlg, top);
687 } else if (event == EVENT_ACTION) {
689 if (!ssd->midsession &&
690 (ctrl == ssd->listbox ||
691 (ssd->loadbutton && ctrl == ssd->loadbutton))) {
693 * The user has double-clicked a session, or hit Load.
694 * We must load the selected session, and then
695 * terminate the configuration dialog _if_ there was a
696 * double-click on the list box _and_ that session
697 * contains a hostname.
699 if (load_selected_session(ssd, dlg, conf, &mbl) &&
700 (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
701 dlg_end(dlg, 1); /* it's all over, and succeeded */
703 } else if (ctrl == ssd->savebutton) {
704 int isdef = !strcmp(ssd->savedsession, "Default Settings");
705 if (!ssd->savedsession[0]) {
706 int i = dlg_listbox_index(ssd->listbox, dlg);
711 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
712 sfree(ssd->savedsession);
713 ssd->savedsession = dupstr(isdef ? "" :
714 ssd->sesslist.sessions[i]);
717 char *errmsg = save_settings(ssd->savedsession, conf);
719 dlg_error_msg(dlg, errmsg);
723 get_sesslist(&ssd->sesslist, FALSE);
724 get_sesslist(&ssd->sesslist, TRUE);
725 dlg_refresh(ssd->editbox, dlg);
726 dlg_refresh(ssd->listbox, dlg);
727 } else if (!ssd->midsession &&
728 ssd->delbutton && ctrl == ssd->delbutton) {
729 int i = dlg_listbox_index(ssd->listbox, dlg);
733 del_settings(ssd->sesslist.sessions[i]);
734 get_sesslist(&ssd->sesslist, FALSE);
735 get_sesslist(&ssd->sesslist, TRUE);
736 dlg_refresh(ssd->listbox, dlg);
738 } else if (ctrl == ssd->okbutton) {
739 if (ssd->midsession) {
740 /* In a mid-session Change Settings, Apply is always OK. */
745 * Annoying special case. If the `Open' button is
746 * pressed while no host name is currently set, _and_
747 * the session list previously had the focus, _and_
748 * there was a session selected in that which had a
749 * valid host name in it, then load it and go.
751 if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
752 !conf_launchable(conf)) {
753 Conf *conf2 = conf_new();
755 if (!load_selected_session(ssd, dlg, conf2, &mbl)) {
760 /* If at this point we have a valid session, go! */
761 if (mbl && conf_launchable(conf2)) {
762 conf_copy_into(conf, conf2);
772 * Otherwise, do the normal thing: if we have a valid
773 * session, get going.
775 if (conf_launchable(conf)) {
779 } else if (ctrl == ssd->cancelbutton) {
785 struct charclass_data {
786 union control *listbox, *editbox, *button;
789 static void charclass_handler(union control *ctrl, void *dlg,
790 void *data, int event)
792 Conf *conf = (Conf *)data;
793 struct charclass_data *ccd =
794 (struct charclass_data *)ctrl->generic.context.p;
796 if (event == EVENT_REFRESH) {
797 if (ctrl == ccd->listbox) {
799 dlg_update_start(ctrl, dlg);
800 dlg_listbox_clear(ctrl, dlg);
801 for (i = 0; i < 128; i++) {
803 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
804 (i >= 0x21 && i != 0x7F) ? i : ' ',
805 conf_get_int_int(conf, CONF_wordness, i));
806 dlg_listbox_add(ctrl, dlg, str);
808 dlg_update_done(ctrl, dlg);
810 } else if (event == EVENT_ACTION) {
811 if (ctrl == ccd->button) {
814 str = dlg_editbox_get(ccd->editbox, dlg);
817 for (i = 0; i < 128; i++) {
818 if (dlg_listbox_issel(ccd->listbox, dlg, i))
819 conf_set_int_int(conf, CONF_wordness, i, n);
821 dlg_refresh(ccd->listbox, dlg);
827 union control *listbox, *redit, *gedit, *bedit, *button;
830 static const char *const colours[] = {
831 "Default Foreground", "Default Bold Foreground",
832 "Default Background", "Default Bold Background",
833 "Cursor Text", "Cursor Colour",
834 "ANSI Black", "ANSI Black Bold",
835 "ANSI Red", "ANSI Red Bold",
836 "ANSI Green", "ANSI Green Bold",
837 "ANSI Yellow", "ANSI Yellow Bold",
838 "ANSI Blue", "ANSI Blue Bold",
839 "ANSI Magenta", "ANSI Magenta Bold",
840 "ANSI Cyan", "ANSI Cyan Bold",
841 "ANSI White", "ANSI White Bold"
844 static void colour_handler(union control *ctrl, void *dlg,
845 void *data, int event)
847 Conf *conf = (Conf *)data;
848 struct colour_data *cd =
849 (struct colour_data *)ctrl->generic.context.p;
850 int update = FALSE, clear = FALSE, r, g, b;
852 if (event == EVENT_REFRESH) {
853 if (ctrl == cd->listbox) {
855 dlg_update_start(ctrl, dlg);
856 dlg_listbox_clear(ctrl, dlg);
857 for (i = 0; i < lenof(colours); i++)
858 dlg_listbox_add(ctrl, dlg, colours[i]);
859 dlg_update_done(ctrl, dlg);
863 } else if (event == EVENT_SELCHANGE) {
864 if (ctrl == cd->listbox) {
865 /* The user has selected a colour. Update the RGB text. */
866 int i = dlg_listbox_index(ctrl, dlg);
871 r = conf_get_int_int(conf, CONF_colours, i*3+0);
872 g = conf_get_int_int(conf, CONF_colours, i*3+1);
873 b = conf_get_int_int(conf, CONF_colours, i*3+2);
877 } else if (event == EVENT_VALCHANGE) {
878 if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
879 /* The user has changed the colour using the edit boxes. */
883 str = dlg_editbox_get(ctrl, dlg);
886 if (cval > 255) cval = 255;
887 if (cval < 0) cval = 0;
889 i = dlg_listbox_index(cd->listbox, dlg);
891 if (ctrl == cd->redit)
892 conf_set_int_int(conf, CONF_colours, i*3+0, cval);
893 else if (ctrl == cd->gedit)
894 conf_set_int_int(conf, CONF_colours, i*3+1, cval);
895 else if (ctrl == cd->bedit)
896 conf_set_int_int(conf, CONF_colours, i*3+2, cval);
899 } else if (event == EVENT_ACTION) {
900 if (ctrl == cd->button) {
901 int i = dlg_listbox_index(cd->listbox, dlg);
907 * Start a colour selector, which will send us an
908 * EVENT_CALLBACK when it's finished and allow us to
909 * pick up the results.
911 dlg_coloursel_start(ctrl, dlg,
912 conf_get_int_int(conf, CONF_colours, i*3+0),
913 conf_get_int_int(conf, CONF_colours, i*3+1),
914 conf_get_int_int(conf, CONF_colours, i*3+2));
916 } else if (event == EVENT_CALLBACK) {
917 if (ctrl == cd->button) {
918 int i = dlg_listbox_index(cd->listbox, dlg);
920 * Collect the results of the colour selector. Will
921 * return nonzero on success, or zero if the colour
922 * selector did nothing (user hit Cancel, for example).
924 if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
925 conf_set_int_int(conf, CONF_colours, i*3+0, r);
926 conf_set_int_int(conf, CONF_colours, i*3+1, g);
927 conf_set_int_int(conf, CONF_colours, i*3+2, b);
936 dlg_editbox_set(cd->redit, dlg, "");
937 dlg_editbox_set(cd->gedit, dlg, "");
938 dlg_editbox_set(cd->bedit, dlg, "");
941 sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
942 sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
943 sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
948 struct ttymodes_data {
949 union control *modelist, *valradio, *valbox;
950 union control *addbutton, *rembutton, *listbox;
953 static void ttymodes_handler(union control *ctrl, void *dlg,
954 void *data, int event)
956 Conf *conf = (Conf *)data;
957 struct ttymodes_data *td =
958 (struct ttymodes_data *)ctrl->generic.context.p;
960 if (event == EVENT_REFRESH) {
961 if (ctrl == td->listbox) {
963 dlg_update_start(ctrl, dlg);
964 dlg_listbox_clear(ctrl, dlg);
965 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
967 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
968 char *disp = dupprintf("%s\t%s", key,
969 (val[0] == 'A') ? "(auto)" : val+1);
970 dlg_listbox_add(ctrl, dlg, disp);
973 dlg_update_done(ctrl, dlg);
974 } else if (ctrl == td->modelist) {
976 dlg_update_start(ctrl, dlg);
977 dlg_listbox_clear(ctrl, dlg);
978 for (i = 0; ttymodes[i]; i++)
979 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
980 dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
981 dlg_update_done(ctrl, dlg);
982 } else if (ctrl == td->valradio) {
983 dlg_radiobutton_set(ctrl, dlg, 0);
985 } else if (event == EVENT_ACTION) {
986 if (ctrl == td->addbutton) {
987 int ind = dlg_listbox_index(td->modelist, dlg);
989 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
992 /* Construct new entry */
994 str = dlg_editbox_get(td->valbox, dlg);
995 val = dupprintf("%c%s", type, str);
997 conf_set_str_str(conf, CONF_ttymodes, key, val);
999 dlg_refresh(td->listbox, dlg);
1002 } else if (ctrl == td->rembutton) {
1005 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
1006 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
1008 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
1009 if (dlg_listbox_issel(td->listbox, dlg, i)) {
1011 /* Populate controls with entry we're about to
1012 * delete, for ease of editing.
1013 * (If multiple entries were selected, don't
1014 * touch the controls.) */
1017 while (ttymodes[ind]) {
1018 if (!strcmp(ttymodes[ind], key))
1022 dlg_listbox_select(td->modelist, dlg, ind);
1023 dlg_radiobutton_set(td->valradio, dlg,
1025 dlg_editbox_set(td->valbox, dlg, val+1);
1027 conf_del_str_str(conf, CONF_ttymodes, key);
1031 dlg_refresh(td->listbox, dlg);
1036 struct environ_data {
1037 union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
1040 static void environ_handler(union control *ctrl, void *dlg,
1041 void *data, int event)
1043 Conf *conf = (Conf *)data;
1044 struct environ_data *ed =
1045 (struct environ_data *)ctrl->generic.context.p;
1047 if (event == EVENT_REFRESH) {
1048 if (ctrl == ed->listbox) {
1050 dlg_update_start(ctrl, dlg);
1051 dlg_listbox_clear(ctrl, dlg);
1052 for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1054 val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1055 char *p = dupprintf("%s\t%s", key, val);
1056 dlg_listbox_add(ctrl, dlg, p);
1059 dlg_update_done(ctrl, dlg);
1061 } else if (event == EVENT_ACTION) {
1062 if (ctrl == ed->addbutton) {
1063 char *key, *val, *str;
1064 key = dlg_editbox_get(ed->varbox, dlg);
1070 val = dlg_editbox_get(ed->valbox, dlg);
1077 conf_set_str_str(conf, CONF_environmt, key, val);
1078 str = dupcat(key, "\t", val, NULL);
1079 dlg_editbox_set(ed->varbox, dlg, "");
1080 dlg_editbox_set(ed->valbox, dlg, "");
1084 dlg_refresh(ed->listbox, dlg);
1085 } else if (ctrl == ed->rembutton) {
1086 int i = dlg_listbox_index(ed->listbox, dlg);
1092 key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
1094 /* Populate controls with the entry we're about to delete
1095 * for ease of editing */
1096 val = conf_get_str_str(conf, CONF_environmt, key);
1097 dlg_editbox_set(ed->varbox, dlg, key);
1098 dlg_editbox_set(ed->valbox, dlg, val);
1100 conf_del_str_str(conf, CONF_environmt, key);
1103 dlg_refresh(ed->listbox, dlg);
1108 struct portfwd_data {
1109 union control *addbutton, *rembutton, *listbox;
1110 union control *sourcebox, *destbox, *direction;
1112 union control *addressfamily;
1116 static void portfwd_handler(union control *ctrl, void *dlg,
1117 void *data, int event)
1119 Conf *conf = (Conf *)data;
1120 struct portfwd_data *pfd =
1121 (struct portfwd_data *)ctrl->generic.context.p;
1123 if (event == EVENT_REFRESH) {
1124 if (ctrl == pfd->listbox) {
1126 dlg_update_start(ctrl, dlg);
1127 dlg_listbox_clear(ctrl, dlg);
1128 for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
1130 val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
1132 if (!strcmp(val, "D")) {
1135 * A dynamic forwarding is stored as L12345=D or
1136 * 6L12345=D (since it's mutually exclusive with
1137 * L12345=anything else), but displayed as D12345
1138 * to match the fiction that 'Local', 'Remote' and
1139 * 'Dynamic' are three distinct modes and also to
1140 * align with OpenSSH's command line option syntax
1141 * that people will already be used to. So, for
1142 * display purposes, find the L in the key string
1143 * and turn it into a D.
1145 p = dupprintf("%s\t", key);
1149 p = dupprintf("%s\t%s", key, val);
1150 dlg_listbox_add(ctrl, dlg, p);
1153 dlg_update_done(ctrl, dlg);
1154 } else if (ctrl == pfd->direction) {
1158 dlg_radiobutton_set(ctrl, dlg, 0);
1160 } else if (ctrl == pfd->addressfamily) {
1161 dlg_radiobutton_set(ctrl, dlg, 0);
1164 } else if (event == EVENT_ACTION) {
1165 if (ctrl == pfd->addbutton) {
1166 const char *family, *type;
1167 char *src, *key, *val;
1171 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1172 if (whichbutton == 1)
1174 else if (whichbutton == 2)
1180 whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1181 if (whichbutton == 0)
1183 else if (whichbutton == 1)
1188 src = dlg_editbox_get(pfd->sourcebox, dlg);
1190 dlg_error_msg(dlg, "You need to specify a source port number");
1195 val = dlg_editbox_get(pfd->destbox, dlg);
1196 if (!*val || !host_strchr(val, ':')) {
1198 "You need to specify a destination address\n"
1199 "in the form \"host.name:port\"");
1206 val = dupstr("D"); /* special case */
1209 key = dupcat(family, type, src, NULL);
1212 if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1213 dlg_error_msg(dlg, "Specified forwarding already exists");
1215 conf_set_str_str(conf, CONF_portfwd, key, val);
1220 dlg_refresh(pfd->listbox, dlg);
1221 } else if (ctrl == pfd->rembutton) {
1222 int i = dlg_listbox_index(pfd->listbox, dlg);
1229 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1231 static const char *const afs = "A46";
1232 static const char *const dirs = "LRD";
1239 /* Populate controls with the entry we're about to delete
1240 * for ease of editing */
1243 afp = strchr(afs, *p);
1245 idx = afp ? afp-afs : 0;
1250 dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1255 val = conf_get_str_str(conf, CONF_portfwd, key);
1256 if (!strcmp(val, "D")) {
1261 dlg_radiobutton_set(pfd->direction, dlg,
1262 strchr(dirs, dir) - dirs);
1265 dlg_editbox_set(pfd->sourcebox, dlg, p);
1266 dlg_editbox_set(pfd->destbox, dlg, val);
1268 conf_del_str_str(conf, CONF_portfwd, key);
1271 dlg_refresh(pfd->listbox, dlg);
1276 struct manual_hostkey_data {
1277 union control *addbutton, *rembutton, *listbox, *keybox;
1280 static void manual_hostkey_handler(union control *ctrl, void *dlg,
1281 void *data, int event)
1283 Conf *conf = (Conf *)data;
1284 struct manual_hostkey_data *mh =
1285 (struct manual_hostkey_data *)ctrl->generic.context.p;
1287 if (event == EVENT_REFRESH) {
1288 if (ctrl == mh->listbox) {
1290 dlg_update_start(ctrl, dlg);
1291 dlg_listbox_clear(ctrl, dlg);
1292 for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1295 val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
1297 dlg_listbox_add(ctrl, dlg, key);
1299 dlg_update_done(ctrl, dlg);
1301 } else if (event == EVENT_ACTION) {
1302 if (ctrl == mh->addbutton) {
1305 key = dlg_editbox_get(mh->keybox, dlg);
1307 dlg_error_msg(dlg, "You need to specify a host key or "
1313 if (!validate_manual_hostkey(key)) {
1314 dlg_error_msg(dlg, "Host key is not in a valid format");
1315 } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
1317 dlg_error_msg(dlg, "Specified host key is already listed");
1319 conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
1323 dlg_refresh(mh->listbox, dlg);
1324 } else if (ctrl == mh->rembutton) {
1325 int i = dlg_listbox_index(mh->listbox, dlg);
1331 key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
1333 dlg_editbox_set(mh->keybox, dlg, key);
1335 conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
1338 dlg_refresh(mh->listbox, dlg);
1343 void setup_config_box(struct controlbox *b, int midsession,
1344 int protocol, int protcfginfo)
1346 struct controlset *s;
1347 struct sessionsaver_data *ssd;
1348 struct charclass_data *ccd;
1349 struct colour_data *cd;
1350 struct ttymodes_data *td;
1351 struct environ_data *ed;
1352 struct portfwd_data *pfd;
1353 struct manual_hostkey_data *mh;
1357 ssd = (struct sessionsaver_data *)
1358 ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data),
1359 sessionsaver_data_free);
1360 memset(ssd, 0, sizeof(*ssd));
1361 ssd->savedsession = dupstr("");
1362 ssd->midsession = midsession;
1365 * The standard panel that appears at the bottom of all panels:
1366 * Open, Cancel, Apply etc.
1368 s = ctrl_getset(b, "", "", "");
1369 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1370 ssd->okbutton = ctrl_pushbutton(s,
1371 (midsession ? "Apply" : "Open"),
1372 (char)(midsession ? 'a' : 'o'),
1374 sessionsaver_handler, P(ssd));
1375 ssd->okbutton->button.isdefault = TRUE;
1376 ssd->okbutton->generic.column = 3;
1377 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1378 sessionsaver_handler, P(ssd));
1379 ssd->cancelbutton->button.iscancel = TRUE;
1380 ssd->cancelbutton->generic.column = 4;
1381 /* We carefully don't close the 5-column part, so that platform-
1382 * specific add-ons can put extra buttons alongside Open and Cancel. */
1385 * The Session panel.
1387 str = dupprintf("Basic options for your %s session", appname);
1388 ctrl_settitle(b, "Session", str);
1392 struct hostport *hp = (struct hostport *)
1393 ctrl_alloc(b, sizeof(struct hostport));
1395 s = ctrl_getset(b, "Session", "hostport",
1396 "Specify the destination you want to connect to");
1397 ctrl_columns(s, 2, 75, 25);
1398 c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1399 HELPCTX(session_hostname),
1400 config_host_handler, I(0), I(0));
1401 c->generic.column = 0;
1403 c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1404 HELPCTX(session_hostname),
1405 config_port_handler, I(0), I(0));
1406 c->generic.column = 1;
1408 ctrl_columns(s, 1, 100);
1410 if (!backend_from_proto(PROT_SSH)) {
1411 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1412 HELPCTX(session_hostname),
1413 config_protocolbuttons_handler, P(hp),
1414 "Raw", 'w', I(PROT_RAW),
1415 "Telnet", 't', I(PROT_TELNET),
1416 "Rlogin", 'i', I(PROT_RLOGIN),
1419 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1420 HELPCTX(session_hostname),
1421 config_protocolbuttons_handler, P(hp),
1422 "Raw", 'w', I(PROT_RAW),
1423 "Telnet", 't', I(PROT_TELNET),
1424 "Rlogin", 'i', I(PROT_RLOGIN),
1425 "SSH", 's', I(PROT_SSH),
1431 * The Load/Save panel is available even in mid-session.
1433 s = ctrl_getset(b, "Session", "savedsessions",
1434 midsession ? "Save the current session settings" :
1435 "Load, save or delete a stored session");
1436 ctrl_columns(s, 2, 75, 25);
1437 get_sesslist(&ssd->sesslist, TRUE);
1438 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1439 HELPCTX(session_saved),
1440 sessionsaver_handler, P(ssd), P(NULL));
1441 ssd->editbox->generic.column = 0;
1442 /* Reset columns so that the buttons are alongside the list, rather
1443 * than alongside that edit box. */
1444 ctrl_columns(s, 1, 100);
1445 ctrl_columns(s, 2, 75, 25);
1446 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1447 HELPCTX(session_saved),
1448 sessionsaver_handler, P(ssd));
1449 ssd->listbox->generic.column = 0;
1450 ssd->listbox->listbox.height = 7;
1452 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1453 HELPCTX(session_saved),
1454 sessionsaver_handler, P(ssd));
1455 ssd->loadbutton->generic.column = 1;
1457 /* We can't offer the Load button mid-session, as it would allow the
1458 * user to load and subsequently save settings they can't see. (And
1459 * also change otherwise immutable settings underfoot; that probably
1460 * shouldn't be a problem, but.) */
1461 ssd->loadbutton = NULL;
1463 /* "Save" button is permitted mid-session. */
1464 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1465 HELPCTX(session_saved),
1466 sessionsaver_handler, P(ssd));
1467 ssd->savebutton->generic.column = 1;
1469 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1470 HELPCTX(session_saved),
1471 sessionsaver_handler, P(ssd));
1472 ssd->delbutton->generic.column = 1;
1474 /* Disable the Delete button mid-session too, for UI consistency. */
1475 ssd->delbutton = NULL;
1477 ctrl_columns(s, 1, 100);
1479 s = ctrl_getset(b, "Session", "otheropts", NULL);
1480 ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1481 HELPCTX(session_coe),
1482 conf_radiobutton_handler,
1483 I(CONF_close_on_exit),
1484 "Always", I(FORCE_ON),
1485 "Never", I(FORCE_OFF),
1486 "Only on clean exit", I(AUTO), NULL);
1489 * The Session/Logging panel.
1491 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1493 s = ctrl_getset(b, "Session/Logging", "main", NULL);
1495 * The logging buttons change depending on whether SSH packet
1496 * logging can sensibly be available.
1499 const char *sshlogname, *sshrawlogname;
1500 if ((midsession && protocol == PROT_SSH) ||
1501 (!midsession && backend_from_proto(PROT_SSH))) {
1502 sshlogname = "SSH packets";
1503 sshrawlogname = "SSH packets and raw data";
1505 sshlogname = NULL; /* this will disable both buttons */
1506 sshrawlogname = NULL; /* this will just placate optimisers */
1508 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1509 HELPCTX(logging_main),
1510 loggingbuttons_handler,
1512 "None", 't', I(LGTYP_NONE),
1513 "Printable output", 'p', I(LGTYP_ASCII),
1514 "All session output", 'l', I(LGTYP_DEBUG),
1515 sshlogname, 's', I(LGTYP_PACKETS),
1516 sshrawlogname, 'r', I(LGTYP_SSHRAW),
1519 ctrl_filesel(s, "Log file name:", 'f',
1520 NULL, TRUE, "Select session log file name",
1521 HELPCTX(logging_filename),
1522 conf_filesel_handler, I(CONF_logfilename));
1523 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1524 " &T for time, &H for host name, and &P for port number)",
1525 HELPCTX(logging_filename));
1526 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1527 HELPCTX(logging_exists),
1528 conf_radiobutton_handler, I(CONF_logxfovr),
1529 "Always overwrite it", I(LGXF_OVR),
1530 "Always append to the end of it", I(LGXF_APN),
1531 "Ask the user every time", I(LGXF_ASK), NULL);
1532 ctrl_checkbox(s, "Flush log file frequently", 'u',
1533 HELPCTX(logging_flush),
1534 conf_checkbox_handler, I(CONF_logflush));
1536 if ((midsession && protocol == PROT_SSH) ||
1537 (!midsession && backend_from_proto(PROT_SSH))) {
1538 s = ctrl_getset(b, "Session/Logging", "ssh",
1539 "Options specific to SSH packet logging");
1540 ctrl_checkbox(s, "Omit known password fields", 'k',
1541 HELPCTX(logging_ssh_omit_password),
1542 conf_checkbox_handler, I(CONF_logomitpass));
1543 ctrl_checkbox(s, "Omit session data", 'd',
1544 HELPCTX(logging_ssh_omit_data),
1545 conf_checkbox_handler, I(CONF_logomitdata));
1549 * The Terminal panel.
1551 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1553 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1554 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1555 HELPCTX(terminal_autowrap),
1556 conf_checkbox_handler, I(CONF_wrap_mode));
1557 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1558 HELPCTX(terminal_decom),
1559 conf_checkbox_handler, I(CONF_dec_om));
1560 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1561 HELPCTX(terminal_lfhascr),
1562 conf_checkbox_handler, I(CONF_lfhascr));
1563 ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1564 HELPCTX(terminal_crhaslf),
1565 conf_checkbox_handler, I(CONF_crhaslf));
1566 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1567 HELPCTX(terminal_bce),
1568 conf_checkbox_handler, I(CONF_bce));
1569 ctrl_checkbox(s, "Enable blinking text", 'n',
1570 HELPCTX(terminal_blink),
1571 conf_checkbox_handler, I(CONF_blinktext));
1572 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1573 HELPCTX(terminal_answerback),
1574 conf_editbox_handler, I(CONF_answerback), I(1));
1576 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1577 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1578 HELPCTX(terminal_localecho),
1579 conf_radiobutton_handler,I(CONF_localecho),
1581 "Force on", I(FORCE_ON),
1582 "Force off", I(FORCE_OFF), NULL);
1583 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1584 HELPCTX(terminal_localedit),
1585 conf_radiobutton_handler,I(CONF_localedit),
1587 "Force on", I(FORCE_ON),
1588 "Force off", I(FORCE_OFF), NULL);
1590 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1591 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1592 HELPCTX(terminal_printing),
1593 printerbox_handler, P(NULL), P(NULL));
1596 * The Terminal/Keyboard panel.
1598 ctrl_settitle(b, "Terminal/Keyboard",
1599 "Options controlling the effects of keys");
1601 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1602 "Change the sequences sent by:");
1603 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1604 HELPCTX(keyboard_backspace),
1605 conf_radiobutton_handler,
1606 I(CONF_bksp_is_delete),
1607 "Control-H", I(0), "Control-? (127)", I(1), NULL);
1608 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1609 HELPCTX(keyboard_homeend),
1610 conf_radiobutton_handler,
1611 I(CONF_rxvt_homeend),
1612 "Standard", I(0), "rxvt", I(1), NULL);
1613 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1614 HELPCTX(keyboard_funkeys),
1615 conf_radiobutton_handler,
1617 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1618 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1620 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1621 "Application keypad settings:");
1622 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1623 HELPCTX(keyboard_appcursor),
1624 conf_radiobutton_handler,
1626 "Normal", I(0), "Application", I(1), NULL);
1627 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1628 HELPCTX(keyboard_appkeypad),
1629 numeric_keypad_handler, P(NULL),
1630 "Normal", I(0), "Application", I(1), "NetHack", I(2),
1634 * The Terminal/Bell panel.
1636 ctrl_settitle(b, "Terminal/Bell",
1637 "Options controlling the terminal bell");
1639 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1640 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1641 HELPCTX(bell_style),
1642 conf_radiobutton_handler, I(CONF_beep),
1643 "None (bell disabled)", I(BELL_DISABLED),
1644 "Make default system alert sound", I(BELL_DEFAULT),
1645 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1647 s = ctrl_getset(b, "Terminal/Bell", "overload",
1648 "Control the bell overload behaviour");
1649 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1650 HELPCTX(bell_overload),
1651 conf_checkbox_handler, I(CONF_bellovl));
1652 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1653 HELPCTX(bell_overload),
1654 conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1655 ctrl_editbox(s, "... in this many seconds", 't', 20,
1656 HELPCTX(bell_overload),
1657 conf_editbox_handler, I(CONF_bellovl_t),
1659 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1660 HELPCTX(bell_overload));
1661 ctrl_editbox(s, "Seconds of silence required", 's', 20,
1662 HELPCTX(bell_overload),
1663 conf_editbox_handler, I(CONF_bellovl_s),
1667 * The Terminal/Features panel.
1669 ctrl_settitle(b, "Terminal/Features",
1670 "Enabling and disabling advanced terminal features");
1672 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1673 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1674 HELPCTX(features_application),
1675 conf_checkbox_handler, I(CONF_no_applic_c));
1676 ctrl_checkbox(s, "Disable application keypad mode", 'k',
1677 HELPCTX(features_application),
1678 conf_checkbox_handler, I(CONF_no_applic_k));
1679 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1680 HELPCTX(features_mouse),
1681 conf_checkbox_handler, I(CONF_no_mouse_rep));
1682 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1683 HELPCTX(features_resize),
1684 conf_checkbox_handler,
1685 I(CONF_no_remote_resize));
1686 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1687 HELPCTX(features_altscreen),
1688 conf_checkbox_handler, I(CONF_no_alt_screen));
1689 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1690 HELPCTX(features_retitle),
1691 conf_checkbox_handler,
1692 I(CONF_no_remote_wintitle));
1693 ctrl_checkbox(s, "Disable remote-controlled clearing of scrollback", 'e',
1694 HELPCTX(features_clearscroll),
1695 conf_checkbox_handler,
1696 I(CONF_no_remote_clearscroll));
1697 ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1698 HELPCTX(features_qtitle),
1699 conf_radiobutton_handler,
1700 I(CONF_remote_qtitle_action),
1701 "None", I(TITLE_NONE),
1702 "Empty string", I(TITLE_EMPTY),
1703 "Window title", I(TITLE_REAL), NULL);
1704 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1705 HELPCTX(features_dbackspace),
1706 conf_checkbox_handler, I(CONF_no_dbackspace));
1707 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1708 'r', HELPCTX(features_charset), conf_checkbox_handler,
1709 I(CONF_no_remote_charset));
1710 ctrl_checkbox(s, "Disable Arabic text shaping",
1711 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1712 I(CONF_arabicshaping));
1713 ctrl_checkbox(s, "Disable bidirectional text display",
1714 'd', HELPCTX(features_bidi), conf_checkbox_handler,
1720 str = dupprintf("Options controlling %s's window", appname);
1721 ctrl_settitle(b, "Window", str);
1724 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1725 ctrl_columns(s, 2, 50, 50);
1726 c = ctrl_editbox(s, "Columns", 'm', 100,
1727 HELPCTX(window_size),
1728 conf_editbox_handler, I(CONF_width), I(-1));
1729 c->generic.column = 0;
1730 c = ctrl_editbox(s, "Rows", 'r', 100,
1731 HELPCTX(window_size),
1732 conf_editbox_handler, I(CONF_height),I(-1));
1733 c->generic.column = 1;
1734 ctrl_columns(s, 1, 100);
1736 s = ctrl_getset(b, "Window", "scrollback",
1737 "Control the scrollback in the window");
1738 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1739 HELPCTX(window_scrollback),
1740 conf_editbox_handler, I(CONF_savelines), I(-1));
1741 ctrl_checkbox(s, "Display scrollbar", 'd',
1742 HELPCTX(window_scrollback),
1743 conf_checkbox_handler, I(CONF_scrollbar));
1744 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1745 HELPCTX(window_scrollback),
1746 conf_checkbox_handler, I(CONF_scroll_on_key));
1747 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1748 HELPCTX(window_scrollback),
1749 conf_checkbox_handler, I(CONF_scroll_on_disp));
1750 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1751 HELPCTX(window_erased),
1752 conf_checkbox_handler,
1753 I(CONF_erase_to_scrollback));
1756 * The Window/Appearance panel.
1758 str = dupprintf("Configure the appearance of %s's window", appname);
1759 ctrl_settitle(b, "Window/Appearance", str);
1762 s = ctrl_getset(b, "Window/Appearance", "cursor",
1763 "Adjust the use of the cursor");
1764 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1765 HELPCTX(appearance_cursor),
1766 conf_radiobutton_handler,
1767 I(CONF_cursor_type),
1769 "Underline", 'u', I(1),
1770 "Vertical line", 'v', I(2), NULL);
1771 ctrl_checkbox(s, "Cursor blinks", 'b',
1772 HELPCTX(appearance_cursor),
1773 conf_checkbox_handler, I(CONF_blink_cur));
1775 s = ctrl_getset(b, "Window/Appearance", "font",
1777 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1778 HELPCTX(appearance_font),
1779 conf_fontsel_handler, I(CONF_font));
1781 s = ctrl_getset(b, "Window/Appearance", "mouse",
1782 "Adjust the use of the mouse pointer");
1783 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1784 HELPCTX(appearance_hidemouse),
1785 conf_checkbox_handler, I(CONF_hide_mouseptr));
1787 s = ctrl_getset(b, "Window/Appearance", "border",
1788 "Adjust the window border");
1789 ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1790 HELPCTX(appearance_border),
1791 conf_editbox_handler,
1792 I(CONF_window_border), I(-1));
1795 * The Window/Behaviour panel.
1797 str = dupprintf("Configure the behaviour of %s's window", appname);
1798 ctrl_settitle(b, "Window/Behaviour", str);
1801 s = ctrl_getset(b, "Window/Behaviour", "title",
1802 "Adjust the behaviour of the window title");
1803 ctrl_editbox(s, "Window title:", 't', 100,
1804 HELPCTX(appearance_title),
1805 conf_editbox_handler, I(CONF_wintitle), I(1));
1806 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1807 HELPCTX(appearance_title),
1808 conf_checkbox_handler,
1809 I(CHECKBOX_INVERT | CONF_win_name_always));
1811 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1812 ctrl_checkbox(s, "Warn before closing window", 'w',
1813 HELPCTX(behaviour_closewarn),
1814 conf_checkbox_handler, I(CONF_warn_on_close));
1817 * The Window/Translation panel.
1819 ctrl_settitle(b, "Window/Translation",
1820 "Options controlling character set translation");
1822 s = ctrl_getset(b, "Window/Translation", "trans",
1823 "Character set translation");
1824 ctrl_combobox(s, "Remote character set:",
1825 'r', 100, HELPCTX(translation_codepage),
1826 codepage_handler, P(NULL), P(NULL));
1828 s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1829 ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1830 HELPCTX(translation_cjk_ambig_wide),
1831 conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1833 str = dupprintf("Adjust how %s handles line drawing characters", appname);
1834 s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1836 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1837 HELPCTX(translation_linedraw),
1838 conf_radiobutton_handler,
1840 "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1841 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1843 ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1844 HELPCTX(selection_linedraw),
1845 conf_checkbox_handler, I(CONF_rawcnp));
1848 * The Window/Selection panel.
1850 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1852 s = ctrl_getset(b, "Window/Selection", "mouse",
1853 "Control use of mouse");
1854 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1855 HELPCTX(selection_shiftdrag),
1856 conf_checkbox_handler, I(CONF_mouse_override));
1857 ctrl_radiobuttons(s,
1858 "Default selection mode (Alt+drag does the other one):",
1860 HELPCTX(selection_rect),
1861 conf_radiobutton_handler,
1862 I(CONF_rect_select),
1863 "Normal", 'n', I(0),
1864 "Rectangular block", 'r', I(1), NULL);
1866 s = ctrl_getset(b, "Window/Selection", "charclass",
1867 "Control the select-one-word-at-a-time mode");
1868 ccd = (struct charclass_data *)
1869 ctrl_alloc(b, sizeof(struct charclass_data));
1870 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1871 HELPCTX(selection_charclasses),
1872 charclass_handler, P(ccd));
1873 ccd->listbox->listbox.multisel = 1;
1874 ccd->listbox->listbox.ncols = 4;
1875 ccd->listbox->listbox.percentages = snewn(4, int);
1876 ccd->listbox->listbox.percentages[0] = 15;
1877 ccd->listbox->listbox.percentages[1] = 25;
1878 ccd->listbox->listbox.percentages[2] = 20;
1879 ccd->listbox->listbox.percentages[3] = 40;
1880 ctrl_columns(s, 2, 67, 33);
1881 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1882 HELPCTX(selection_charclasses),
1883 charclass_handler, P(ccd), P(NULL));
1884 ccd->editbox->generic.column = 0;
1885 ccd->button = ctrl_pushbutton(s, "Set", 's',
1886 HELPCTX(selection_charclasses),
1887 charclass_handler, P(ccd));
1888 ccd->button->generic.column = 1;
1889 ctrl_columns(s, 1, 100);
1892 * The Window/Colours panel.
1894 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1896 s = ctrl_getset(b, "Window/Colours", "general",
1897 "General options for colour usage");
1898 ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1899 HELPCTX(colours_ansi),
1900 conf_checkbox_handler, I(CONF_ansi_colour));
1901 ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1902 HELPCTX(colours_xterm256), conf_checkbox_handler,
1903 I(CONF_xterm_256_colour));
1904 ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
1905 HELPCTX(colours_bold),
1906 conf_radiobutton_handler, I(CONF_bold_style),
1912 str = dupprintf("Adjust the precise colours %s displays", appname);
1913 s = ctrl_getset(b, "Window/Colours", "adjust", str);
1915 ctrl_text(s, "Select a colour from the list, and then click the"
1916 " Modify button to change its appearance.",
1917 HELPCTX(colours_config));
1918 ctrl_columns(s, 2, 67, 33);
1919 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1920 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1921 HELPCTX(colours_config), colour_handler, P(cd));
1922 cd->listbox->generic.column = 0;
1923 cd->listbox->listbox.height = 7;
1924 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1925 c->generic.column = 1;
1926 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1927 colour_handler, P(cd), P(NULL));
1928 cd->redit->generic.column = 1;
1929 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1930 colour_handler, P(cd), P(NULL));
1931 cd->gedit->generic.column = 1;
1932 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1933 colour_handler, P(cd), P(NULL));
1934 cd->bedit->generic.column = 1;
1935 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1936 colour_handler, P(cd));
1937 cd->button->generic.column = 1;
1938 ctrl_columns(s, 1, 100);
1941 * The Connection panel. This doesn't show up if we're in a
1942 * non-network utility such as pterm. We tell this by being
1943 * passed a protocol < 0.
1945 if (protocol >= 0) {
1946 ctrl_settitle(b, "Connection", "Options controlling the connection");
1948 s = ctrl_getset(b, "Connection", "keepalive",
1949 "Sending of null packets to keep session active");
1950 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1951 HELPCTX(connection_keepalive),
1952 conf_editbox_handler, I(CONF_ping_interval),
1956 s = ctrl_getset(b, "Connection", "tcp",
1957 "Low-level TCP connection options");
1958 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1959 'n', HELPCTX(connection_nodelay),
1960 conf_checkbox_handler,
1961 I(CONF_tcp_nodelay));
1962 ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1963 'p', HELPCTX(connection_tcpkeepalive),
1964 conf_checkbox_handler,
1965 I(CONF_tcp_keepalives));
1967 s = ctrl_getset(b, "Connection", "ipversion",
1968 "Internet protocol version");
1969 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1970 HELPCTX(connection_ipversion),
1971 conf_radiobutton_handler,
1972 I(CONF_addressfamily),
1973 "Auto", 'u', I(ADDRTYPE_UNSPEC),
1974 "IPv4", '4', I(ADDRTYPE_IPV4),
1975 "IPv6", '6', I(ADDRTYPE_IPV6),
1980 const char *label = backend_from_proto(PROT_SSH) ?
1981 "Logical name of remote host (e.g. for SSH key lookup):" :
1982 "Logical name of remote host:";
1983 s = ctrl_getset(b, "Connection", "identity",
1984 "Logical name of remote host");
1985 ctrl_editbox(s, label, 'm', 100,
1986 HELPCTX(connection_loghost),
1987 conf_editbox_handler, I(CONF_loghost), I(1));
1992 * A sub-panel Connection/Data, containing options that
1993 * decide on data to send to the server.
1996 ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1998 s = ctrl_getset(b, "Connection/Data", "login",
2000 ctrl_editbox(s, "Auto-login username", 'u', 50,
2001 HELPCTX(connection_username),
2002 conf_editbox_handler, I(CONF_username), I(1));
2004 /* We assume the local username is sufficiently stable
2005 * to include on the dialog box. */
2006 char *user = get_username();
2007 char *userlabel = dupprintf("Use system username (%s)",
2010 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
2011 HELPCTX(connection_username_from_env),
2012 conf_radiobutton_handler,
2013 I(CONF_username_from_env),
2020 s = ctrl_getset(b, "Connection/Data", "term",
2021 "Terminal details");
2022 ctrl_editbox(s, "Terminal-type string", 't', 50,
2023 HELPCTX(connection_termtype),
2024 conf_editbox_handler, I(CONF_termtype), I(1));
2025 ctrl_editbox(s, "Terminal speeds", 's', 50,
2026 HELPCTX(connection_termspeed),
2027 conf_editbox_handler, I(CONF_termspeed), I(1));
2029 s = ctrl_getset(b, "Connection/Data", "env",
2030 "Environment variables");
2031 ctrl_columns(s, 2, 80, 20);
2032 ed = (struct environ_data *)
2033 ctrl_alloc(b, sizeof(struct environ_data));
2034 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
2035 HELPCTX(telnet_environ),
2036 environ_handler, P(ed), P(NULL));
2037 ed->varbox->generic.column = 0;
2038 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
2039 HELPCTX(telnet_environ),
2040 environ_handler, P(ed), P(NULL));
2041 ed->valbox->generic.column = 0;
2042 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
2043 HELPCTX(telnet_environ),
2044 environ_handler, P(ed));
2045 ed->addbutton->generic.column = 1;
2046 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2047 HELPCTX(telnet_environ),
2048 environ_handler, P(ed));
2049 ed->rembutton->generic.column = 1;
2050 ctrl_columns(s, 1, 100);
2051 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2052 HELPCTX(telnet_environ),
2053 environ_handler, P(ed));
2054 ed->listbox->listbox.height = 3;
2055 ed->listbox->listbox.ncols = 2;
2056 ed->listbox->listbox.percentages = snewn(2, int);
2057 ed->listbox->listbox.percentages[0] = 30;
2058 ed->listbox->listbox.percentages[1] = 70;
2065 * The Connection/Proxy panel.
2067 ctrl_settitle(b, "Connection/Proxy",
2068 "Options controlling proxy usage");
2070 s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
2071 ctrl_radiobuttons(s, "Proxy type:", 't', 3,
2072 HELPCTX(proxy_type),
2073 conf_radiobutton_handler,
2075 "None", I(PROXY_NONE),
2076 "SOCKS 4", I(PROXY_SOCKS4),
2077 "SOCKS 5", I(PROXY_SOCKS5),
2078 "HTTP", I(PROXY_HTTP),
2079 "Telnet", I(PROXY_TELNET),
2081 ctrl_columns(s, 2, 80, 20);
2082 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
2083 HELPCTX(proxy_main),
2084 conf_editbox_handler,
2085 I(CONF_proxy_host), I(1));
2086 c->generic.column = 0;
2087 c = ctrl_editbox(s, "Port", 'p', 100,
2088 HELPCTX(proxy_main),
2089 conf_editbox_handler,
2092 c->generic.column = 1;
2093 ctrl_columns(s, 1, 100);
2094 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
2095 HELPCTX(proxy_exclude),
2096 conf_editbox_handler,
2097 I(CONF_proxy_exclude_list), I(1));
2098 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
2099 HELPCTX(proxy_exclude),
2100 conf_checkbox_handler,
2101 I(CONF_even_proxy_localhost));
2102 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
2104 conf_radiobutton_handler,
2108 "Yes", I(FORCE_ON), NULL);
2109 ctrl_editbox(s, "Username", 'u', 60,
2110 HELPCTX(proxy_auth),
2111 conf_editbox_handler,
2112 I(CONF_proxy_username), I(1));
2113 c = ctrl_editbox(s, "Password", 'w', 60,
2114 HELPCTX(proxy_auth),
2115 conf_editbox_handler,
2116 I(CONF_proxy_password), I(1));
2117 c->editbox.password = 1;
2118 ctrl_editbox(s, "Telnet command", 'm', 100,
2119 HELPCTX(proxy_command),
2120 conf_editbox_handler,
2121 I(CONF_proxy_telnet_command), I(1));
2123 ctrl_radiobuttons(s, "Print proxy diagnostics "
2124 "in the terminal window", 'r', 5,
2125 HELPCTX(proxy_main),
2126 conf_radiobutton_handler,
2127 I(CONF_proxy_log_to_term),
2130 "Only until session starts", I(AUTO), NULL);
2134 * The Telnet panel exists in the base config box, and in a
2135 * mid-session reconfig box _if_ we're using Telnet.
2137 if (!midsession || protocol == PROT_TELNET) {
2139 * The Connection/Telnet panel.
2141 ctrl_settitle(b, "Connection/Telnet",
2142 "Options controlling Telnet connections");
2144 s = ctrl_getset(b, "Connection/Telnet", "protocol",
2145 "Telnet protocol adjustments");
2148 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2150 HELPCTX(telnet_oldenviron),
2151 conf_radiobutton_handler,
2152 I(CONF_rfc_environ),
2153 "BSD (commonplace)", 'b', I(0),
2154 "RFC 1408 (unusual)", 'f', I(1), NULL);
2155 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2156 HELPCTX(telnet_passive),
2157 conf_radiobutton_handler,
2158 I(CONF_passive_telnet),
2159 "Passive", I(1), "Active", I(0), NULL);
2161 ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2162 HELPCTX(telnet_specialkeys),
2163 conf_checkbox_handler,
2164 I(CONF_telnet_keyboard));
2165 ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2166 'm', HELPCTX(telnet_newline),
2167 conf_checkbox_handler,
2168 I(CONF_telnet_newline));
2174 * The Connection/Rlogin panel.
2176 ctrl_settitle(b, "Connection/Rlogin",
2177 "Options controlling Rlogin connections");
2179 s = ctrl_getset(b, "Connection/Rlogin", "data",
2180 "Data to send to the server");
2181 ctrl_editbox(s, "Local username:", 'l', 50,
2182 HELPCTX(rlogin_localuser),
2183 conf_editbox_handler, I(CONF_localusername), I(1));
2188 * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2189 * when we're not doing SSH.
2192 if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2195 * The Connection/SSH panel.
2197 ctrl_settitle(b, "Connection/SSH",
2198 "Options controlling SSH connections");
2200 /* SSH-1 or connection-sharing downstream */
2201 if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
2202 s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2203 ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2204 "session; it is only here so that sub-panels of it can "
2205 "exist without looking strange.", HELPCTX(no_help));
2210 s = ctrl_getset(b, "Connection/SSH", "data",
2211 "Data to send to the server");
2212 ctrl_editbox(s, "Remote command:", 'r', 100,
2213 HELPCTX(ssh_command),
2214 conf_editbox_handler, I(CONF_remote_cmd), I(1));
2216 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2217 ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2218 HELPCTX(ssh_noshell),
2219 conf_checkbox_handler,
2220 I(CONF_ssh_no_shell));
2223 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2224 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2226 ctrl_checkbox(s, "Enable compression", 'e',
2227 HELPCTX(ssh_compress),
2228 conf_checkbox_handler,
2229 I(CONF_compression));
2233 s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
2235 ctrl_checkbox(s, "Share SSH connections if possible", 's',
2237 conf_checkbox_handler,
2238 I(CONF_ssh_connection_sharing));
2240 ctrl_text(s, "Permitted roles in a shared connection:",
2241 HELPCTX(ssh_share));
2242 ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
2244 conf_checkbox_handler,
2245 I(CONF_ssh_connection_sharing_upstream));
2246 ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
2248 conf_checkbox_handler,
2249 I(CONF_ssh_connection_sharing_downstream));
2253 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2255 ctrl_radiobuttons(s, "SSH protocol version:", NO_SHORTCUT, 2,
2256 HELPCTX(ssh_protocol),
2257 conf_radiobutton_handler,
2260 "1 (INSECURE)", '1', I(0), NULL);
2264 * The Connection/SSH/Kex panel. (Owing to repeat key
2265 * exchange, much of this is meaningful in mid-session _if_
2266 * we're using SSH-2 and are not a connection-sharing
2267 * downstream, or haven't decided yet.)
2269 if (protcfginfo != 1 && protcfginfo != -1) {
2270 ctrl_settitle(b, "Connection/SSH/Kex",
2271 "Options controlling SSH key exchange");
2273 s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2274 "Key exchange algorithm options");
2275 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2276 HELPCTX(ssh_kexlist),
2277 kexlist_handler, P(NULL));
2278 c->listbox.height = 5;
2280 s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2281 "Options controlling key re-exchange");
2283 ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2284 HELPCTX(ssh_kex_repeat),
2285 conf_editbox_handler,
2286 I(CONF_ssh_rekey_time),
2288 ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2289 HELPCTX(ssh_kex_repeat),
2290 conf_editbox_handler,
2291 I(CONF_ssh_rekey_data),
2293 ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2294 HELPCTX(ssh_kex_repeat));
2298 * The 'Connection/SSH/Host keys' panel.
2300 if (protcfginfo != 1 && protcfginfo != -1) {
2301 ctrl_settitle(b, "Connection/SSH/Host keys",
2302 "Options controlling SSH host keys");
2304 s = ctrl_getset(b, "Connection/SSH/Host keys", "main",
2305 "Host key algorithm preference");
2306 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2307 HELPCTX(ssh_hklist),
2308 hklist_handler, P(NULL));
2309 c->listbox.height = 5;
2313 * Manual host key configuration is irrelevant mid-session,
2314 * as we enforce that the host key for rekeys is the
2315 * same as that used at the start of the session.
2318 s = ctrl_getset(b, "Connection/SSH/Host keys", "hostkeys",
2319 "Manually configure host keys for this connection");
2321 ctrl_columns(s, 2, 75, 25);
2322 c = ctrl_text(s, "Host keys or fingerprints to accept:",
2323 HELPCTX(ssh_kex_manual_hostkeys));
2324 c->generic.column = 0;
2325 /* You want to select from the list, _then_ hit Remove. So
2326 * tab order should be that way round. */
2327 mh = (struct manual_hostkey_data *)
2328 ctrl_alloc(b,sizeof(struct manual_hostkey_data));
2329 mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2330 HELPCTX(ssh_kex_manual_hostkeys),
2331 manual_hostkey_handler, P(mh));
2332 mh->rembutton->generic.column = 1;
2333 mh->rembutton->generic.tabdelay = 1;
2334 mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2335 HELPCTX(ssh_kex_manual_hostkeys),
2336 manual_hostkey_handler, P(mh));
2337 /* This list box can't be very tall, because there's not
2338 * much room in the pane on Windows at least. This makes
2339 * it become really unhelpful if a horizontal scrollbar
2340 * appears, so we suppress that. */
2341 mh->listbox->listbox.height = 2;
2342 mh->listbox->listbox.hscroll = FALSE;
2343 ctrl_tabdelay(s, mh->rembutton);
2344 mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
2345 HELPCTX(ssh_kex_manual_hostkeys),
2346 manual_hostkey_handler, P(mh), P(NULL));
2347 mh->keybox->generic.column = 0;
2348 mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
2349 HELPCTX(ssh_kex_manual_hostkeys),
2350 manual_hostkey_handler, P(mh));
2351 mh->addbutton->generic.column = 1;
2352 ctrl_columns(s, 1, 100);
2355 if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
2357 * The Connection/SSH/Cipher panel.
2359 ctrl_settitle(b, "Connection/SSH/Cipher",
2360 "Options controlling SSH encryption");
2362 s = ctrl_getset(b, "Connection/SSH/Cipher",
2363 "encryption", "Encryption options");
2364 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2365 HELPCTX(ssh_ciphers),
2366 cipherlist_handler, P(NULL));
2367 c->listbox.height = 6;
2369 ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2370 HELPCTX(ssh_ciphers),
2371 conf_checkbox_handler,
2372 I(CONF_ssh2_des_cbc));
2378 * The Connection/SSH/Auth panel.
2380 ctrl_settitle(b, "Connection/SSH/Auth",
2381 "Options controlling SSH authentication");
2383 s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2384 ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2385 'd', HELPCTX(ssh_auth_banner),
2386 conf_checkbox_handler,
2387 I(CONF_ssh_show_banner));
2388 ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2389 HELPCTX(ssh_auth_bypass),
2390 conf_checkbox_handler,
2391 I(CONF_ssh_no_userauth));
2393 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2394 "Authentication methods");
2395 ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2396 HELPCTX(ssh_auth_pageant),
2397 conf_checkbox_handler,
2399 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2400 HELPCTX(ssh_auth_tis),
2401 conf_checkbox_handler,
2402 I(CONF_try_tis_auth));
2403 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2404 'i', HELPCTX(ssh_auth_ki),
2405 conf_checkbox_handler,
2406 I(CONF_try_ki_auth));
2408 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2409 "Authentication parameters");
2410 ctrl_checkbox(s, "Allow agent forwarding", 'f',
2411 HELPCTX(ssh_auth_agentfwd),
2412 conf_checkbox_handler, I(CONF_agentfwd));
2413 ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2414 HELPCTX(ssh_auth_changeuser),
2415 conf_checkbox_handler,
2416 I(CONF_change_username));
2417 ctrl_filesel(s, "Private key file for authentication:", 'k',
2418 FILTER_KEY_FILES, FALSE, "Select private key file",
2419 HELPCTX(ssh_auth_privkey),
2420 conf_filesel_handler, I(CONF_keyfile));
2424 * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2425 * the main Auth panel.
2427 ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2428 "Options controlling GSSAPI authentication");
2429 s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2431 ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2432 't', HELPCTX(ssh_gssapi),
2433 conf_checkbox_handler,
2434 I(CONF_try_gssapi_auth));
2436 ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2437 HELPCTX(ssh_gssapi_delegation),
2438 conf_checkbox_handler,
2442 * GSSAPI library selection.
2445 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2446 'p', HELPCTX(ssh_gssapi_libraries),
2447 gsslist_handler, P(NULL));
2448 c->listbox.height = ngsslibs;
2451 * I currently assume that if more than one GSS
2452 * library option is available, then one of them is
2453 * 'user-supplied' and so we should present the
2454 * following file selector. This is at least half-
2455 * reasonable, because if we're using statically
2456 * linked GSSAPI then there will only be one option
2457 * and no way to load from a user-supplied library,
2458 * whereas if we're using dynamic libraries then
2459 * there will almost certainly be some default
2460 * option in addition to a user-supplied path. If
2461 * anyone ever ports PuTTY to a system on which
2462 * dynamic-library GSSAPI is available but there is
2463 * absolutely no consensus on where to keep the
2464 * libraries, there'll need to be a flag alongside
2465 * ngsslibs to control whether the file selector is
2469 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2470 FILTER_DYNLIB_FILES, FALSE, "Select library file",
2471 HELPCTX(ssh_gssapi_libraries),
2472 conf_filesel_handler,
2473 I(CONF_ssh_gss_custom));
2480 * The Connection/SSH/TTY panel.
2482 ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2484 s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2485 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2487 conf_checkbox_handler,
2490 s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2492 td = (struct ttymodes_data *)
2493 ctrl_alloc(b, sizeof(struct ttymodes_data));
2494 ctrl_columns(s, 2, 75, 25);
2495 c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2496 c->generic.column = 0;
2497 td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2498 HELPCTX(ssh_ttymodes),
2499 ttymodes_handler, P(td));
2500 td->rembutton->generic.column = 1;
2501 td->rembutton->generic.tabdelay = 1;
2502 ctrl_columns(s, 1, 100);
2503 td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2504 HELPCTX(ssh_ttymodes),
2505 ttymodes_handler, P(td));
2506 td->listbox->listbox.multisel = 1;
2507 td->listbox->listbox.height = 4;
2508 td->listbox->listbox.ncols = 2;
2509 td->listbox->listbox.percentages = snewn(2, int);
2510 td->listbox->listbox.percentages[0] = 40;
2511 td->listbox->listbox.percentages[1] = 60;
2512 ctrl_tabdelay(s, td->rembutton);
2513 ctrl_columns(s, 2, 75, 25);
2514 td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2515 HELPCTX(ssh_ttymodes),
2516 ttymodes_handler, P(td));
2517 td->modelist->generic.column = 0;
2518 td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2519 HELPCTX(ssh_ttymodes),
2520 ttymodes_handler, P(td));
2521 td->addbutton->generic.column = 1;
2522 td->addbutton->generic.tabdelay = 1;
2523 ctrl_columns(s, 1, 100); /* column break */
2524 /* Bit of a hack to get the value radio buttons and
2525 * edit-box on the same row. */
2526 ctrl_columns(s, 3, 25, 50, 25);
2527 c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2528 c->generic.column = 0;
2529 td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2530 HELPCTX(ssh_ttymodes),
2531 ttymodes_handler, P(td),
2532 "Auto", NO_SHORTCUT, P(NULL),
2533 "This:", NO_SHORTCUT, P(NULL),
2535 td->valradio->generic.column = 1;
2536 td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2537 HELPCTX(ssh_ttymodes),
2538 ttymodes_handler, P(td), P(NULL));
2539 td->valbox->generic.column = 2;
2540 ctrl_tabdelay(s, td->addbutton);
2546 * The Connection/SSH/X11 panel.
2548 ctrl_settitle(b, "Connection/SSH/X11",
2549 "Options controlling SSH X11 forwarding");
2551 s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2552 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2553 HELPCTX(ssh_tunnels_x11),
2554 conf_checkbox_handler,I(CONF_x11_forward));
2555 ctrl_editbox(s, "X display location", 'x', 50,
2556 HELPCTX(ssh_tunnels_x11),
2557 conf_editbox_handler, I(CONF_x11_display), I(1));
2558 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2559 HELPCTX(ssh_tunnels_x11auth),
2560 conf_radiobutton_handler,
2562 "MIT-Magic-Cookie-1", I(X11_MIT),
2563 "XDM-Authorization-1", I(X11_XDM), NULL);
2567 * The Tunnels panel _is_ still available in mid-session.
2569 ctrl_settitle(b, "Connection/SSH/Tunnels",
2570 "Options controlling SSH port forwarding");
2572 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2574 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2575 HELPCTX(ssh_tunnels_portfwd_localhost),
2576 conf_checkbox_handler,
2577 I(CONF_lport_acceptall));
2578 ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2579 HELPCTX(ssh_tunnels_portfwd_localhost),
2580 conf_checkbox_handler,
2581 I(CONF_rport_acceptall));
2583 ctrl_columns(s, 3, 55, 20, 25);
2584 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2585 c->generic.column = COLUMN_FIELD(0,2);
2586 /* You want to select from the list, _then_ hit Remove. So tab order
2587 * should be that way round. */
2588 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2589 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2590 HELPCTX(ssh_tunnels_portfwd),
2591 portfwd_handler, P(pfd));
2592 pfd->rembutton->generic.column = 2;
2593 pfd->rembutton->generic.tabdelay = 1;
2594 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2595 HELPCTX(ssh_tunnels_portfwd),
2596 portfwd_handler, P(pfd));
2597 pfd->listbox->listbox.height = 3;
2598 pfd->listbox->listbox.ncols = 2;
2599 pfd->listbox->listbox.percentages = snewn(2, int);
2600 pfd->listbox->listbox.percentages[0] = 20;
2601 pfd->listbox->listbox.percentages[1] = 80;
2602 ctrl_tabdelay(s, pfd->rembutton);
2603 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2604 /* You want to enter source, destination and type, _then_ hit Add.
2605 * Again, we adjust the tab order to reflect this. */
2606 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2607 HELPCTX(ssh_tunnels_portfwd),
2608 portfwd_handler, P(pfd));
2609 pfd->addbutton->generic.column = 2;
2610 pfd->addbutton->generic.tabdelay = 1;
2611 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2612 HELPCTX(ssh_tunnels_portfwd),
2613 portfwd_handler, P(pfd), P(NULL));
2614 pfd->sourcebox->generic.column = 0;
2615 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2616 HELPCTX(ssh_tunnels_portfwd),
2617 portfwd_handler, P(pfd), P(NULL));
2618 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2619 HELPCTX(ssh_tunnels_portfwd),
2620 portfwd_handler, P(pfd),
2621 "Local", 'l', P(NULL),
2622 "Remote", 'm', P(NULL),
2623 "Dynamic", 'y', P(NULL),
2626 pfd->addressfamily =
2627 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2628 HELPCTX(ssh_tunnels_portfwd_ipversion),
2629 portfwd_handler, P(pfd),
2630 "Auto", 'u', I(ADDRTYPE_UNSPEC),
2631 "IPv4", '4', I(ADDRTYPE_IPV4),
2632 "IPv6", '6', I(ADDRTYPE_IPV6),
2635 ctrl_tabdelay(s, pfd->addbutton);
2636 ctrl_columns(s, 1, 100);
2640 * The Connection/SSH/Bugs panels.
2642 ctrl_settitle(b, "Connection/SSH/Bugs",
2643 "Workarounds for SSH server bugs");
2645 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2646 "Detection of known bugs in SSH servers");
2647 ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2648 HELPCTX(ssh_bugs_ignore2),
2649 sshbug_handler, I(CONF_sshbug_ignore2));
2650 ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2651 HELPCTX(ssh_bugs_rekey2),
2652 sshbug_handler, I(CONF_sshbug_rekey2));
2653 ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
2654 20, HELPCTX(ssh_bugs_winadj),
2655 sshbug_handler, I(CONF_sshbug_winadj));
2656 ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
2657 HELPCTX(ssh_bugs_chanreq),
2658 sshbug_handler, I(CONF_sshbug_chanreq));
2659 ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2660 HELPCTX(ssh_bugs_maxpkt2),
2661 sshbug_handler, I(CONF_sshbug_maxpkt2));
2663 ctrl_settitle(b, "Connection/SSH/More bugs",
2664 "Further workarounds for SSH server bugs");
2666 s = ctrl_getset(b, "Connection/SSH/More bugs", "main",
2667 "Detection of known bugs in SSH servers");
2668 ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2669 HELPCTX(ssh_bugs_rsapad2),
2670 sshbug_handler, I(CONF_sshbug_rsapad2));
2671 ctrl_droplist(s, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20,
2672 HELPCTX(ssh_bugs_oldgex2),
2673 sshbug_handler, I(CONF_sshbug_oldgex2));
2674 ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2675 HELPCTX(ssh_bugs_hmac2),
2676 sshbug_handler, I(CONF_sshbug_hmac2));
2677 ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2678 HELPCTX(ssh_bugs_pksessid2),
2679 sshbug_handler, I(CONF_sshbug_pksessid2));
2680 ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2681 HELPCTX(ssh_bugs_derivekey2),
2682 sshbug_handler, I(CONF_sshbug_derivekey2));
2683 ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2684 HELPCTX(ssh_bugs_ignore1),
2685 sshbug_handler, I(CONF_sshbug_ignore1));
2686 ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2687 HELPCTX(ssh_bugs_plainpw1),
2688 sshbug_handler, I(CONF_sshbug_plainpw1));
2689 ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2690 HELPCTX(ssh_bugs_rsa1),
2691 sshbug_handler, I(CONF_sshbug_rsa1));