2 * config.c - the platform-independent parts of the PuTTY
13 #define PRINTER_DISABLED_STRING "None (printing disabled)"
15 #define HOST_BOX_TITLE "Host Name (or IP address)"
16 #define PORT_BOX_TITLE "Port"
18 void conf_radiobutton_handler(union control *ctrl, void *dlg,
19 void *data, int event)
22 Conf *conf = (Conf *)data;
25 * For a standard radio button set, the context parameter gives
26 * the primary key (CONF_foo), and the extra data per button
27 * gives the value the target field should take if that button
28 * is the one selected.
30 if (event == EVENT_REFRESH) {
31 int val = conf_get_int(conf, ctrl->radio.context.i);
32 for (button = 0; button < ctrl->radio.nbuttons; button++)
33 if (val == ctrl->radio.buttondata[button].i)
35 /* We expected that `break' to happen, in all circumstances. */
36 assert(button < ctrl->radio.nbuttons);
37 dlg_radiobutton_set(ctrl, dlg, button);
38 } else if (event == EVENT_VALCHANGE) {
39 button = dlg_radiobutton_get(ctrl, dlg);
40 assert(button >= 0 && button < ctrl->radio.nbuttons);
41 conf_set_int(conf, ctrl->radio.context.i,
42 ctrl->radio.buttondata[button].i);
46 #define CHECKBOX_INVERT (1<<30)
47 void conf_checkbox_handler(union control *ctrl, void *dlg,
48 void *data, int event)
51 Conf *conf = (Conf *)data;
54 * For a standard checkbox, the context parameter gives the
55 * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
57 key = ctrl->checkbox.context.i;
58 if (key & CHECKBOX_INVERT) {
59 key &= ~CHECKBOX_INVERT;
65 * C lacks a logical XOR, so the following code uses the idiom
66 * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
67 * iff exactly one of a and b is nonzero, otherwise 0.)
70 if (event == EVENT_REFRESH) {
71 int val = conf_get_int(conf, key);
72 dlg_checkbox_set(ctrl, dlg, (!val ^ !invert));
73 } else if (event == EVENT_VALCHANGE) {
74 conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert);
78 void conf_editbox_handler(union control *ctrl, void *dlg,
79 void *data, int event)
82 * The standard edit-box handler expects the main `context'
83 * field to contain the primary key. The secondary `context2'
84 * field indicates the type of this field:
86 * - if context2 > 0, the field is a string.
87 * - if context2 == -1, the field is an int and the edit box
89 * - if context2 < -1, the field is an int and the edit box is
90 * _floating_, and (-context2) gives the scale. (E.g. if
91 * context2 == -1000, then typing 1.2 into the box will set
94 int key = ctrl->editbox.context.i;
95 int length = ctrl->editbox.context2.i;
96 Conf *conf = (Conf *)data;
99 if (event == EVENT_REFRESH) {
100 char *field = conf_get_str(conf, key);
101 dlg_editbox_set(ctrl, dlg, field);
102 } else if (event == EVENT_VALCHANGE) {
103 char *field = dlg_editbox_get(ctrl, dlg);
104 conf_set_str(conf, key, field);
107 } else if (length < 0) {
108 if (event == EVENT_REFRESH) {
110 int value = conf_get_int(conf, key);
112 sprintf(str, "%d", value);
114 sprintf(str, "%g", (double)value / (double)(-length));
115 dlg_editbox_set(ctrl, dlg, str);
116 } else if (event == EVENT_VALCHANGE) {
117 char *str = dlg_editbox_get(ctrl, dlg);
119 conf_set_int(conf, key, atoi(str));
121 conf_set_int(conf, key, (int)((-length) * atof(str)));
127 void conf_filesel_handler(union control *ctrl, void *dlg,
128 void *data, int event)
130 int key = ctrl->fileselect.context.i;
131 Conf *conf = (Conf *)data;
133 if (event == EVENT_REFRESH) {
134 dlg_filesel_set(ctrl, dlg, conf_get_filename(conf, key));
135 } else if (event == EVENT_VALCHANGE) {
136 Filename *filename = dlg_filesel_get(ctrl, dlg);
137 conf_set_filename(conf, key, filename);
138 filename_free(filename);
142 void conf_fontsel_handler(union control *ctrl, void *dlg,
143 void *data, int event)
145 int key = ctrl->fontselect.context.i;
146 Conf *conf = (Conf *)data;
148 if (event == EVENT_REFRESH) {
149 dlg_fontsel_set(ctrl, dlg, conf_get_fontspec(conf, key));
150 } else if (event == EVENT_VALCHANGE) {
151 FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg);
152 conf_set_fontspec(conf, key, fontspec);
153 fontspec_free(fontspec);
157 static void config_host_handler(union control *ctrl, void *dlg,
158 void *data, int event)
160 Conf *conf = (Conf *)data;
163 * This function works just like the standard edit box handler,
164 * only it has to choose the control's label and text from two
165 * different places depending on the protocol.
167 if (event == EVENT_REFRESH) {
168 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
170 * This label text is carefully chosen to contain an n,
171 * since that's the shortcut for the host name control.
173 dlg_label_change(ctrl, dlg, "Serial line");
174 dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
176 dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
177 dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
179 } else if (event == EVENT_VALCHANGE) {
180 char *s = dlg_editbox_get(ctrl, dlg);
181 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
182 conf_set_str(conf, CONF_serline, s);
184 conf_set_str(conf, CONF_host, s);
189 static void config_port_handler(union control *ctrl, void *dlg,
190 void *data, int event)
192 Conf *conf = (Conf *)data;
196 * This function works similarly to the standard edit box handler,
197 * only it has to choose the control's label and text from two
198 * different places depending on the protocol.
200 if (event == EVENT_REFRESH) {
201 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
203 * This label text is carefully chosen to contain a p,
204 * since that's the shortcut for the port control.
206 dlg_label_change(ctrl, dlg, "Speed");
207 sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
209 dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
210 if (conf_get_int(conf, CONF_port) != 0)
211 sprintf(buf, "%d", conf_get_int(conf, CONF_port));
213 /* Display an (invalid) port of 0 as blank */
216 dlg_editbox_set(ctrl, dlg, buf);
217 } else if (event == EVENT_VALCHANGE) {
218 char *s = dlg_editbox_get(ctrl, dlg);
222 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
223 conf_set_int(conf, CONF_serspeed, i);
225 conf_set_int(conf, CONF_port, i);
230 union control *host, *port;
234 * We export this function so that platform-specific config
235 * routines can use it to conveniently identify the protocol radio
236 * buttons in order to add to them.
238 void config_protocolbuttons_handler(union control *ctrl, void *dlg,
239 void *data, int event)
242 Conf *conf = (Conf *)data;
243 struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
246 * This function works just like the standard radio-button
247 * handler, except that it also has to change the setting of
248 * the port box, and refresh both host and port boxes when. We
249 * expect the context parameter to point at a hostport
250 * structure giving the `union control's for both.
252 if (event == EVENT_REFRESH) {
253 int protocol = conf_get_int(conf, CONF_protocol);
254 for (button = 0; button < ctrl->radio.nbuttons; button++)
255 if (protocol == ctrl->radio.buttondata[button].i)
257 /* We expected that `break' to happen, in all circumstances. */
258 assert(button < ctrl->radio.nbuttons);
259 dlg_radiobutton_set(ctrl, dlg, button);
260 } else if (event == EVENT_VALCHANGE) {
261 int oldproto = conf_get_int(conf, CONF_protocol);
264 button = dlg_radiobutton_get(ctrl, dlg);
265 assert(button >= 0 && button < ctrl->radio.nbuttons);
266 newproto = ctrl->radio.buttondata[button].i;
267 conf_set_int(conf, CONF_protocol, newproto);
269 if (oldproto != newproto) {
270 Backend *ob = backend_from_proto(oldproto);
271 Backend *nb = backend_from_proto(newproto);
274 /* Iff the user hasn't changed the port from the old protocol's
275 * default, update it with the new protocol's default.
276 * (This includes a "default" of 0, implying that there is no
277 * sensible default for that protocol; in this case it's
278 * displayed as a blank.)
279 * This helps with the common case of tabbing through the
280 * controls in order and setting a non-default port before
281 * getting to the protocol; we want that non-default port
282 * to be preserved. */
283 port = conf_get_int(conf, CONF_port);
284 if (port == ob->default_port)
285 conf_set_int(conf, CONF_port, nb->default_port);
287 dlg_refresh(hp->host, dlg);
288 dlg_refresh(hp->port, dlg);
292 static void loggingbuttons_handler(union control *ctrl, void *dlg,
293 void *data, int event)
296 Conf *conf = (Conf *)data;
297 /* This function works just like the standard radio-button handler,
298 * but it has to fall back to "no logging" in situations where the
299 * configured logging type isn't applicable.
301 if (event == EVENT_REFRESH) {
302 int logtype = conf_get_int(conf, CONF_logtype);
304 for (button = 0; button < ctrl->radio.nbuttons; button++)
305 if (logtype == ctrl->radio.buttondata[button].i)
308 /* We fell off the end, so we lack the configured logging type */
309 if (button == ctrl->radio.nbuttons) {
311 conf_set_int(conf, CONF_logtype, LGTYP_NONE);
313 dlg_radiobutton_set(ctrl, dlg, button);
314 } else if (event == EVENT_VALCHANGE) {
315 button = dlg_radiobutton_get(ctrl, dlg);
316 assert(button >= 0 && button < ctrl->radio.nbuttons);
317 conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
321 static void numeric_keypad_handler(union control *ctrl, void *dlg,
322 void *data, int event)
325 Conf *conf = (Conf *)data;
327 * This function works much like the standard radio button
328 * handler, but it has to handle two fields in Conf.
330 if (event == EVENT_REFRESH) {
331 if (conf_get_int(conf, CONF_nethack_keypad))
333 else if (conf_get_int(conf, CONF_app_keypad))
337 assert(button < ctrl->radio.nbuttons);
338 dlg_radiobutton_set(ctrl, dlg, button);
339 } else if (event == EVENT_VALCHANGE) {
340 button = dlg_radiobutton_get(ctrl, dlg);
341 assert(button >= 0 && button < ctrl->radio.nbuttons);
343 conf_set_int(conf, CONF_app_keypad, FALSE);
344 conf_set_int(conf, CONF_nethack_keypad, TRUE);
346 conf_set_int(conf, CONF_app_keypad, (button != 0));
347 conf_set_int(conf, CONF_nethack_keypad, FALSE);
352 static void cipherlist_handler(union control *ctrl, void *dlg,
353 void *data, int event)
355 Conf *conf = (Conf *)data;
356 if (event == EVENT_REFRESH) {
359 static const struct { char *s; int c; } ciphers[] = {
360 { "3DES", CIPHER_3DES },
361 { "Blowfish", CIPHER_BLOWFISH },
362 { "DES", CIPHER_DES },
363 { "AES (SSH-2 only)", CIPHER_AES },
364 { "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
365 { "-- warn below here --", CIPHER_WARN }
368 /* Set up the "selected ciphers" box. */
369 /* (cipherlist assumed to contain all ciphers) */
370 dlg_update_start(ctrl, dlg);
371 dlg_listbox_clear(ctrl, dlg);
372 for (i = 0; i < CIPHER_MAX; i++) {
373 int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
376 for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
377 if (ciphers[j].c == c) {
382 dlg_listbox_addwithid(ctrl, dlg, cstr, c);
384 dlg_update_done(ctrl, dlg);
386 } else if (event == EVENT_VALCHANGE) {
389 /* Update array to match the list box. */
390 for (i=0; i < CIPHER_MAX; i++)
391 conf_set_int_int(conf, CONF_ssh_cipherlist, i,
392 dlg_listbox_getid(ctrl, dlg, i));
397 static void gsslist_handler(union control *ctrl, void *dlg,
398 void *data, int event)
400 Conf *conf = (Conf *)data;
401 if (event == EVENT_REFRESH) {
404 dlg_update_start(ctrl, dlg);
405 dlg_listbox_clear(ctrl, dlg);
406 for (i = 0; i < ngsslibs; i++) {
407 int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
408 assert(id >= 0 && id < ngsslibs);
409 dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
411 dlg_update_done(ctrl, dlg);
413 } else if (event == EVENT_VALCHANGE) {
416 /* Update array to match the list box. */
417 for (i=0; i < ngsslibs; i++)
418 conf_set_int_int(conf, CONF_ssh_gsslist, i,
419 dlg_listbox_getid(ctrl, dlg, i));
424 static void kexlist_handler(union control *ctrl, void *dlg,
425 void *data, int event)
427 Conf *conf = (Conf *)data;
428 if (event == EVENT_REFRESH) {
431 static const struct { char *s; int k; } kexes[] = {
432 { "Diffie-Hellman group 1", KEX_DHGROUP1 },
433 { "Diffie-Hellman group 14", KEX_DHGROUP14 },
434 { "Diffie-Hellman group exchange", KEX_DHGEX },
435 { "RSA-based key exchange", KEX_RSA },
436 { "-- warn below here --", KEX_WARN }
439 /* Set up the "kex preference" box. */
440 /* (kexlist assumed to contain all algorithms) */
441 dlg_update_start(ctrl, dlg);
442 dlg_listbox_clear(ctrl, dlg);
443 for (i = 0; i < KEX_MAX; i++) {
444 int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
447 for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
448 if (kexes[j].k == k) {
453 dlg_listbox_addwithid(ctrl, dlg, kstr, k);
455 dlg_update_done(ctrl, dlg);
457 } else if (event == EVENT_VALCHANGE) {
460 /* Update array to match the list box. */
461 for (i=0; i < KEX_MAX; i++)
462 conf_set_int_int(conf, CONF_ssh_kexlist, i,
463 dlg_listbox_getid(ctrl, dlg, i));
467 static void printerbox_handler(union control *ctrl, void *dlg,
468 void *data, int event)
470 Conf *conf = (Conf *)data;
471 if (event == EVENT_REFRESH) {
476 dlg_update_start(ctrl, dlg);
478 * Some backends may wish to disable the drop-down list on
479 * this edit box. Be prepared for this.
481 if (ctrl->editbox.has_list) {
482 dlg_listbox_clear(ctrl, dlg);
483 dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
484 pe = printer_start_enum(&nprinters);
485 for (i = 0; i < nprinters; i++)
486 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
487 printer_finish_enum(pe);
489 printer = conf_get_str(conf, CONF_printer);
491 printer = PRINTER_DISABLED_STRING;
492 dlg_editbox_set(ctrl, dlg, printer);
493 dlg_update_done(ctrl, dlg);
494 } else if (event == EVENT_VALCHANGE) {
495 char *printer = dlg_editbox_get(ctrl, dlg);
496 if (!strcmp(printer, PRINTER_DISABLED_STRING))
498 conf_set_str(conf, CONF_printer, printer);
503 static void codepage_handler(union control *ctrl, void *dlg,
504 void *data, int event)
506 Conf *conf = (Conf *)data;
507 if (event == EVENT_REFRESH) {
509 const char *cp, *thiscp;
510 dlg_update_start(ctrl, dlg);
511 thiscp = cp_name(decode_codepage(conf_get_str(conf,
512 CONF_line_codepage)));
513 dlg_listbox_clear(ctrl, dlg);
514 for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
515 dlg_listbox_add(ctrl, dlg, cp);
516 dlg_editbox_set(ctrl, dlg, thiscp);
517 conf_set_str(conf, CONF_line_codepage, thiscp);
518 dlg_update_done(ctrl, dlg);
519 } else if (event == EVENT_VALCHANGE) {
520 char *codepage = dlg_editbox_get(ctrl, dlg);
521 conf_set_str(conf, CONF_line_codepage,
522 cp_name(decode_codepage(codepage)));
527 static void sshbug_handler(union control *ctrl, void *dlg,
528 void *data, int event)
530 Conf *conf = (Conf *)data;
531 if (event == EVENT_REFRESH) {
532 dlg_update_start(ctrl, dlg);
533 dlg_listbox_clear(ctrl, dlg);
534 dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
535 dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
536 dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
537 switch (conf_get_int(conf, ctrl->listbox.context.i)) {
538 case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
539 case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
540 case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
542 dlg_update_done(ctrl, dlg);
543 } else if (event == EVENT_SELCHANGE) {
544 int i = dlg_listbox_index(ctrl, dlg);
548 i = dlg_listbox_getid(ctrl, dlg, i);
549 conf_set_int(conf, ctrl->listbox.context.i, i);
553 #define SAVEDSESSION_LEN 2048
555 struct sessionsaver_data {
556 union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
557 union control *okbutton, *cancelbutton;
558 struct sesslist sesslist;
563 * Helper function to load the session selected in the list box, if
564 * any, as this is done in more than one place below. Returns 0 for
567 static int load_selected_session(struct sessionsaver_data *ssd,
569 void *dlg, Conf *conf, int *maybe_launch)
571 int i = dlg_listbox_index(ssd->listbox, dlg);
577 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
578 load_settings(ssd->sesslist.sessions[i], conf);
580 strncpy(savedsession, ssd->sesslist.sessions[i],
582 savedsession[SAVEDSESSION_LEN-1] = '\0';
584 *maybe_launch = TRUE;
586 savedsession[0] = '\0';
588 *maybe_launch = FALSE;
590 dlg_refresh(NULL, dlg);
591 /* Restore the selection, which might have been clobbered by
592 * changing the value of the edit box. */
593 dlg_listbox_select(ssd->listbox, dlg, i);
597 static void sessionsaver_handler(union control *ctrl, void *dlg,
598 void *data, int event)
600 Conf *conf = (Conf *)data;
601 struct sessionsaver_data *ssd =
602 (struct sessionsaver_data *)ctrl->generic.context.p;
606 * The first time we're called in a new dialog, we must
607 * allocate space to store the current contents of the saved
608 * session edit box (since it must persist even when we switch
609 * panels, but is not part of the Conf).
611 * FIXME: this is disgusting, and we'd do much better to have
612 * the persistent storage be dynamically allocated and get rid
613 * of the arbitrary limit SAVEDSESSION_LEN. To do that would
614 * require a means of making sure the memory gets freed at the
615 * appropriate moment.
619 } else if (!dlg_get_privdata(ssd->editbox, dlg)) {
620 savedsession = (char *)
621 dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN);
622 savedsession[0] = '\0';
624 savedsession = dlg_get_privdata(ssd->editbox, dlg);
627 if (event == EVENT_REFRESH) {
628 if (ctrl == ssd->editbox) {
629 dlg_editbox_set(ctrl, dlg, savedsession);
630 } else if (ctrl == ssd->listbox) {
632 dlg_update_start(ctrl, dlg);
633 dlg_listbox_clear(ctrl, dlg);
634 for (i = 0; i < ssd->sesslist.nsessions; i++)
635 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
636 dlg_update_done(ctrl, dlg);
638 } else if (event == EVENT_VALCHANGE) {
639 int top, bottom, halfway, i;
640 if (ctrl == ssd->editbox) {
641 char *tmp = dlg_editbox_get(ctrl, dlg);
642 strncpy(savedsession, tmp, SAVEDSESSION_LEN);
644 top = ssd->sesslist.nsessions;
646 while (top-bottom > 1) {
647 halfway = (top+bottom)/2;
648 i = strcmp(savedsession, ssd->sesslist.sessions[halfway]);
655 if (top == ssd->sesslist.nsessions) {
658 dlg_listbox_select(ssd->listbox, dlg, top);
660 } else if (event == EVENT_ACTION) {
662 if (!ssd->midsession &&
663 (ctrl == ssd->listbox ||
664 (ssd->loadbutton && ctrl == ssd->loadbutton))) {
666 * The user has double-clicked a session, or hit Load.
667 * We must load the selected session, and then
668 * terminate the configuration dialog _if_ there was a
669 * double-click on the list box _and_ that session
670 * contains a hostname.
672 if (load_selected_session(ssd, savedsession, dlg, conf, &mbl) &&
673 (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
674 dlg_end(dlg, 1); /* it's all over, and succeeded */
676 } else if (ctrl == ssd->savebutton) {
677 int isdef = !strcmp(savedsession, "Default Settings");
678 if (!savedsession[0]) {
679 int i = dlg_listbox_index(ssd->listbox, dlg);
684 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
686 strncpy(savedsession, ssd->sesslist.sessions[i],
688 savedsession[SAVEDSESSION_LEN-1] = '\0';
690 savedsession[0] = '\0';
694 char *errmsg = save_settings(savedsession, conf);
696 dlg_error_msg(dlg, errmsg);
700 get_sesslist(&ssd->sesslist, FALSE);
701 get_sesslist(&ssd->sesslist, TRUE);
702 dlg_refresh(ssd->editbox, dlg);
703 dlg_refresh(ssd->listbox, dlg);
704 } else if (!ssd->midsession &&
705 ssd->delbutton && ctrl == ssd->delbutton) {
706 int i = dlg_listbox_index(ssd->listbox, dlg);
710 del_settings(ssd->sesslist.sessions[i]);
711 get_sesslist(&ssd->sesslist, FALSE);
712 get_sesslist(&ssd->sesslist, TRUE);
713 dlg_refresh(ssd->listbox, dlg);
715 } else if (ctrl == ssd->okbutton) {
716 if (ssd->midsession) {
717 /* In a mid-session Change Settings, Apply is always OK. */
722 * Annoying special case. If the `Open' button is
723 * pressed while no host name is currently set, _and_
724 * the session list previously had the focus, _and_
725 * there was a session selected in that which had a
726 * valid host name in it, then load it and go.
728 if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
729 !conf_launchable(conf)) {
730 Conf *conf2 = conf_new();
732 if (!load_selected_session(ssd, savedsession, dlg,
738 /* If at this point we have a valid session, go! */
739 if (mbl && conf_launchable(conf2)) {
740 conf_copy_into(conf, conf2);
750 * Otherwise, do the normal thing: if we have a valid
751 * session, get going.
753 if (conf_launchable(conf)) {
757 } else if (ctrl == ssd->cancelbutton) {
763 struct charclass_data {
764 union control *listbox, *editbox, *button;
767 static void charclass_handler(union control *ctrl, void *dlg,
768 void *data, int event)
770 Conf *conf = (Conf *)data;
771 struct charclass_data *ccd =
772 (struct charclass_data *)ctrl->generic.context.p;
774 if (event == EVENT_REFRESH) {
775 if (ctrl == ccd->listbox) {
777 dlg_update_start(ctrl, dlg);
778 dlg_listbox_clear(ctrl, dlg);
779 for (i = 0; i < 128; i++) {
781 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
782 (i >= 0x21 && i != 0x7F) ? i : ' ',
783 conf_get_int_int(conf, CONF_wordness, i));
784 dlg_listbox_add(ctrl, dlg, str);
786 dlg_update_done(ctrl, dlg);
788 } else if (event == EVENT_ACTION) {
789 if (ctrl == ccd->button) {
792 str = dlg_editbox_get(ccd->editbox, dlg);
795 for (i = 0; i < 128; i++) {
796 if (dlg_listbox_issel(ccd->listbox, dlg, i))
797 conf_set_int_int(conf, CONF_wordness, i, n);
799 dlg_refresh(ccd->listbox, dlg);
805 union control *listbox, *redit, *gedit, *bedit, *button;
808 static const char *const colours[] = {
809 "Default Foreground", "Default Bold Foreground",
810 "Default Background", "Default Bold Background",
811 "Cursor Text", "Cursor Colour",
812 "ANSI Black", "ANSI Black Bold",
813 "ANSI Red", "ANSI Red Bold",
814 "ANSI Green", "ANSI Green Bold",
815 "ANSI Yellow", "ANSI Yellow Bold",
816 "ANSI Blue", "ANSI Blue Bold",
817 "ANSI Magenta", "ANSI Magenta Bold",
818 "ANSI Cyan", "ANSI Cyan Bold",
819 "ANSI White", "ANSI White Bold"
822 static void colour_handler(union control *ctrl, void *dlg,
823 void *data, int event)
825 Conf *conf = (Conf *)data;
826 struct colour_data *cd =
827 (struct colour_data *)ctrl->generic.context.p;
828 int update = FALSE, clear = FALSE, r, g, b;
830 if (event == EVENT_REFRESH) {
831 if (ctrl == cd->listbox) {
833 dlg_update_start(ctrl, dlg);
834 dlg_listbox_clear(ctrl, dlg);
835 for (i = 0; i < lenof(colours); i++)
836 dlg_listbox_add(ctrl, dlg, colours[i]);
837 dlg_update_done(ctrl, dlg);
841 } else if (event == EVENT_SELCHANGE) {
842 if (ctrl == cd->listbox) {
843 /* The user has selected a colour. Update the RGB text. */
844 int i = dlg_listbox_index(ctrl, dlg);
849 r = conf_get_int_int(conf, CONF_colours, i*3+0);
850 g = conf_get_int_int(conf, CONF_colours, i*3+0);
851 b = conf_get_int_int(conf, CONF_colours, i*3+0);
855 } else if (event == EVENT_VALCHANGE) {
856 if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
857 /* The user has changed the colour using the edit boxes. */
861 str = dlg_editbox_get(ctrl, dlg);
864 if (cval > 255) cval = 255;
865 if (cval < 0) cval = 0;
867 i = dlg_listbox_index(cd->listbox, dlg);
869 if (ctrl == cd->redit)
870 conf_set_int_int(conf, CONF_colours, i*3+0, cval);
871 else if (ctrl == cd->gedit)
872 conf_set_int_int(conf, CONF_colours, i*3+1, cval);
873 else if (ctrl == cd->bedit)
874 conf_set_int_int(conf, CONF_colours, i*3+2, cval);
877 } else if (event == EVENT_ACTION) {
878 if (ctrl == cd->button) {
879 int i = dlg_listbox_index(cd->listbox, dlg);
885 * Start a colour selector, which will send us an
886 * EVENT_CALLBACK when it's finished and allow us to
887 * pick up the results.
889 dlg_coloursel_start(ctrl, dlg,
890 conf_get_int_int(conf, CONF_colours, i*3+0),
891 conf_get_int_int(conf, CONF_colours, i*3+1),
892 conf_get_int_int(conf, CONF_colours, i*3+2));
894 } else if (event == EVENT_CALLBACK) {
895 if (ctrl == cd->button) {
896 int i = dlg_listbox_index(cd->listbox, dlg);
898 * Collect the results of the colour selector. Will
899 * return nonzero on success, or zero if the colour
900 * selector did nothing (user hit Cancel, for example).
902 if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
903 conf_set_int_int(conf, CONF_colours, i*3+0, r);
904 conf_set_int_int(conf, CONF_colours, i*3+0, g);
905 conf_set_int_int(conf, CONF_colours, i*3+0, b);
914 dlg_editbox_set(cd->redit, dlg, "");
915 dlg_editbox_set(cd->gedit, dlg, "");
916 dlg_editbox_set(cd->bedit, dlg, "");
919 sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
920 sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
921 sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
926 struct ttymodes_data {
927 union control *modelist, *valradio, *valbox;
928 union control *addbutton, *rembutton, *listbox;
931 static void ttymodes_handler(union control *ctrl, void *dlg,
932 void *data, int event)
934 Conf *conf = (Conf *)data;
935 struct ttymodes_data *td =
936 (struct ttymodes_data *)ctrl->generic.context.p;
938 if (event == EVENT_REFRESH) {
939 if (ctrl == td->listbox) {
941 dlg_update_start(ctrl, dlg);
942 dlg_listbox_clear(ctrl, dlg);
943 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
945 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
946 char *disp = dupprintf("%s\t%s", key,
947 (val[0] == 'A') ? "(auto)" : val+1);
948 dlg_listbox_add(ctrl, dlg, disp);
951 dlg_update_done(ctrl, dlg);
952 } else if (ctrl == td->modelist) {
954 dlg_update_start(ctrl, dlg);
955 dlg_listbox_clear(ctrl, dlg);
956 for (i = 0; ttymodes[i]; i++)
957 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
958 dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
959 dlg_update_done(ctrl, dlg);
960 } else if (ctrl == td->valradio) {
961 dlg_radiobutton_set(ctrl, dlg, 0);
963 } else if (event == EVENT_ACTION) {
964 if (ctrl == td->addbutton) {
965 int ind = dlg_listbox_index(td->modelist, dlg);
967 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
970 /* Construct new entry */
972 str = dlg_editbox_get(td->valbox, dlg);
973 val = dupprintf("%c%s", type, str);
975 conf_set_str_str(conf, CONF_ttymodes, key, val);
977 dlg_refresh(td->listbox, dlg);
980 } else if (ctrl == td->rembutton) {
983 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
984 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
986 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
987 if (dlg_listbox_issel(td->listbox, dlg, i)) {
989 /* Populate controls with entry we're about to
990 * delete, for ease of editing.
991 * (If multiple entries were selected, don't
992 * touch the controls.) */
995 while (ttymodes[ind]) {
996 if (!strcmp(ttymodes[ind], key))
1000 dlg_listbox_select(td->modelist, dlg, ind);
1001 dlg_radiobutton_set(td->valradio, dlg,
1003 dlg_editbox_set(td->valbox, dlg, val+1);
1005 conf_del_str_str(conf, CONF_ttymodes, key);
1009 dlg_refresh(td->listbox, dlg);
1014 struct environ_data {
1015 union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
1018 static void environ_handler(union control *ctrl, void *dlg,
1019 void *data, int event)
1021 Conf *conf = (Conf *)data;
1022 struct environ_data *ed =
1023 (struct environ_data *)ctrl->generic.context.p;
1025 if (event == EVENT_REFRESH) {
1026 if (ctrl == ed->listbox) {
1028 dlg_update_start(ctrl, dlg);
1029 dlg_listbox_clear(ctrl, dlg);
1030 for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1032 val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1033 char *p = dupprintf("%s\t%s", key, val);
1034 dlg_listbox_add(ctrl, dlg, p);
1037 dlg_update_done(ctrl, dlg);
1039 } else if (event == EVENT_ACTION) {
1040 if (ctrl == ed->addbutton) {
1041 char *key, *val, *str;
1042 key = dlg_editbox_get(ed->varbox, dlg);
1048 val = dlg_editbox_get(ed->valbox, dlg);
1055 conf_set_str_str(conf, CONF_environmt, key, val);
1056 str = dupcat(key, "\t", val, NULL);
1057 dlg_editbox_set(ed->varbox, dlg, "");
1058 dlg_editbox_set(ed->valbox, dlg, "");
1062 dlg_refresh(ed->listbox, dlg);
1063 } else if (ctrl == ed->rembutton) {
1064 int i = dlg_listbox_index(ed->listbox, dlg);
1070 key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
1072 /* Populate controls with the entry we're about to delete
1073 * for ease of editing */
1074 val = conf_get_str_str(conf, CONF_environmt, key);
1075 dlg_editbox_set(ed->varbox, dlg, key);
1076 dlg_editbox_set(ed->valbox, dlg, val);
1078 conf_del_str_str(conf, CONF_environmt, key);
1081 dlg_refresh(ed->listbox, dlg);
1086 struct portfwd_data {
1087 union control *addbutton, *rembutton, *listbox;
1088 union control *sourcebox, *destbox, *direction;
1090 union control *addressfamily;
1094 static void portfwd_handler(union control *ctrl, void *dlg,
1095 void *data, int event)
1097 Conf *conf = (Conf *)data;
1098 struct portfwd_data *pfd =
1099 (struct portfwd_data *)ctrl->generic.context.p;
1101 if (event == EVENT_REFRESH) {
1102 if (ctrl == pfd->listbox) {
1104 dlg_update_start(ctrl, dlg);
1105 dlg_listbox_clear(ctrl, dlg);
1106 for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
1108 val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
1110 if (!strcmp(val, "D"))
1111 p = dupprintf("D%s\t", key+1);
1113 p = dupprintf("%s\t%s", key, val);
1114 dlg_listbox_add(ctrl, dlg, p);
1117 dlg_update_done(ctrl, dlg);
1118 } else if (ctrl == pfd->direction) {
1122 dlg_radiobutton_set(ctrl, dlg, 0);
1124 } else if (ctrl == pfd->addressfamily) {
1125 dlg_radiobutton_set(ctrl, dlg, 0);
1128 } else if (event == EVENT_ACTION) {
1129 if (ctrl == pfd->addbutton) {
1130 char *family, *type, *src, *key, *val;
1135 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1136 if (whichbutton == 1)
1138 else if (whichbutton == 2)
1144 whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1145 if (whichbutton == 0)
1147 else if (whichbutton == 1)
1152 src = dlg_editbox_get(pfd->sourcebox, dlg);
1154 dlg_error_msg(dlg, "You need to specify a source port number");
1159 val = dlg_editbox_get(pfd->destbox, dlg);
1160 if (!*val || !strchr(val, ':')) {
1162 "You need to specify a destination address\n"
1163 "in the form \"host.name:port\"");
1170 val = dupstr("D"); /* special case */
1173 key = dupcat(family, type, src, NULL);
1176 if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1177 dlg_error_msg(dlg, "Specified forwarding already exists");
1179 conf_set_str_str(conf, CONF_portfwd, key, val);
1184 dlg_refresh(pfd->listbox, dlg);
1185 } else if (ctrl == pfd->rembutton) {
1186 int i = dlg_listbox_index(pfd->listbox, dlg);
1190 char *key, *val, *p;
1192 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1194 static const char *const afs = "A46";
1195 static const char *const dirs = "LRD";
1202 /* Populate controls with the entry we're about to delete
1203 * for ease of editing */
1206 afp = strchr(afs, *p);
1208 idx = afp ? afp-afs : 0;
1213 dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1218 val = conf_get_str_str(conf, CONF_portfwd, key);
1219 if (!strcmp(val, "D")) {
1224 dlg_radiobutton_set(pfd->direction, dlg,
1225 strchr(dirs, dir) - dirs);
1228 dlg_editbox_set(pfd->sourcebox, dlg, p);
1229 dlg_editbox_set(pfd->destbox, dlg, val);
1231 conf_del_str_str(conf, CONF_portfwd, key);
1234 dlg_refresh(pfd->listbox, dlg);
1239 void setup_config_box(struct controlbox *b, int midsession,
1240 int protocol, int protcfginfo)
1242 struct controlset *s;
1243 struct sessionsaver_data *ssd;
1244 struct charclass_data *ccd;
1245 struct colour_data *cd;
1246 struct ttymodes_data *td;
1247 struct environ_data *ed;
1248 struct portfwd_data *pfd;
1252 ssd = (struct sessionsaver_data *)
1253 ctrl_alloc(b, sizeof(struct sessionsaver_data));
1254 memset(ssd, 0, sizeof(*ssd));
1255 ssd->midsession = midsession;
1258 * The standard panel that appears at the bottom of all panels:
1259 * Open, Cancel, Apply etc.
1261 s = ctrl_getset(b, "", "", "");
1262 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1263 ssd->okbutton = ctrl_pushbutton(s,
1264 (midsession ? "Apply" : "Open"),
1265 (char)(midsession ? 'a' : 'o'),
1267 sessionsaver_handler, P(ssd));
1268 ssd->okbutton->button.isdefault = TRUE;
1269 ssd->okbutton->generic.column = 3;
1270 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1271 sessionsaver_handler, P(ssd));
1272 ssd->cancelbutton->button.iscancel = TRUE;
1273 ssd->cancelbutton->generic.column = 4;
1274 /* We carefully don't close the 5-column part, so that platform-
1275 * specific add-ons can put extra buttons alongside Open and Cancel. */
1278 * The Session panel.
1280 str = dupprintf("Basic options for your %s session", appname);
1281 ctrl_settitle(b, "Session", str);
1285 struct hostport *hp = (struct hostport *)
1286 ctrl_alloc(b, sizeof(struct hostport));
1288 s = ctrl_getset(b, "Session", "hostport",
1289 "Specify the destination you want to connect to");
1290 ctrl_columns(s, 2, 75, 25);
1291 c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1292 HELPCTX(session_hostname),
1293 config_host_handler, I(0), I(0));
1294 c->generic.column = 0;
1296 c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1297 HELPCTX(session_hostname),
1298 config_port_handler, I(0), I(0));
1299 c->generic.column = 1;
1301 ctrl_columns(s, 1, 100);
1303 if (!backend_from_proto(PROT_SSH)) {
1304 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1305 HELPCTX(session_hostname),
1306 config_protocolbuttons_handler, P(hp),
1307 "Raw", 'w', I(PROT_RAW),
1308 "Telnet", 't', I(PROT_TELNET),
1309 "Rlogin", 'i', I(PROT_RLOGIN),
1312 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1313 HELPCTX(session_hostname),
1314 config_protocolbuttons_handler, P(hp),
1315 "Raw", 'w', I(PROT_RAW),
1316 "Telnet", 't', I(PROT_TELNET),
1317 "Rlogin", 'i', I(PROT_RLOGIN),
1318 "SSH", 's', I(PROT_SSH),
1324 * The Load/Save panel is available even in mid-session.
1326 s = ctrl_getset(b, "Session", "savedsessions",
1327 midsession ? "Save the current session settings" :
1328 "Load, save or delete a stored session");
1329 ctrl_columns(s, 2, 75, 25);
1330 get_sesslist(&ssd->sesslist, TRUE);
1331 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1332 HELPCTX(session_saved),
1333 sessionsaver_handler, P(ssd), P(NULL));
1334 ssd->editbox->generic.column = 0;
1335 /* Reset columns so that the buttons are alongside the list, rather
1336 * than alongside that edit box. */
1337 ctrl_columns(s, 1, 100);
1338 ctrl_columns(s, 2, 75, 25);
1339 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1340 HELPCTX(session_saved),
1341 sessionsaver_handler, P(ssd));
1342 ssd->listbox->generic.column = 0;
1343 ssd->listbox->listbox.height = 7;
1345 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1346 HELPCTX(session_saved),
1347 sessionsaver_handler, P(ssd));
1348 ssd->loadbutton->generic.column = 1;
1350 /* We can't offer the Load button mid-session, as it would allow the
1351 * user to load and subsequently save settings they can't see. (And
1352 * also change otherwise immutable settings underfoot; that probably
1353 * shouldn't be a problem, but.) */
1354 ssd->loadbutton = NULL;
1356 /* "Save" button is permitted mid-session. */
1357 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1358 HELPCTX(session_saved),
1359 sessionsaver_handler, P(ssd));
1360 ssd->savebutton->generic.column = 1;
1362 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1363 HELPCTX(session_saved),
1364 sessionsaver_handler, P(ssd));
1365 ssd->delbutton->generic.column = 1;
1367 /* Disable the Delete button mid-session too, for UI consistency. */
1368 ssd->delbutton = NULL;
1370 ctrl_columns(s, 1, 100);
1372 s = ctrl_getset(b, "Session", "otheropts", NULL);
1373 c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1374 HELPCTX(session_coe),
1375 conf_radiobutton_handler,
1376 I(CONF_close_on_exit),
1377 "Always", I(FORCE_ON),
1378 "Never", I(FORCE_OFF),
1379 "Only on clean exit", I(AUTO), NULL);
1382 * The Session/Logging panel.
1384 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1386 s = ctrl_getset(b, "Session/Logging", "main", NULL);
1388 * The logging buttons change depending on whether SSH packet
1389 * logging can sensibly be available.
1392 char *sshlogname, *sshrawlogname;
1393 if ((midsession && protocol == PROT_SSH) ||
1394 (!midsession && backend_from_proto(PROT_SSH))) {
1395 sshlogname = "SSH packets";
1396 sshrawlogname = "SSH packets and raw data";
1398 sshlogname = NULL; /* this will disable both buttons */
1399 sshrawlogname = NULL; /* this will just placate optimisers */
1401 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1402 HELPCTX(logging_main),
1403 loggingbuttons_handler,
1405 "None", 't', I(LGTYP_NONE),
1406 "Printable output", 'p', I(LGTYP_ASCII),
1407 "All session output", 'l', I(LGTYP_DEBUG),
1408 sshlogname, 's', I(LGTYP_PACKETS),
1409 sshrawlogname, 'r', I(LGTYP_SSHRAW),
1412 ctrl_filesel(s, "Log file name:", 'f',
1413 NULL, TRUE, "Select session log file name",
1414 HELPCTX(logging_filename),
1415 conf_filesel_handler, I(CONF_logfilename));
1416 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1417 " &T for time, and &H for host name)",
1418 HELPCTX(logging_filename));
1419 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1420 HELPCTX(logging_exists),
1421 conf_radiobutton_handler, I(CONF_logxfovr),
1422 "Always overwrite it", I(LGXF_OVR),
1423 "Always append to the end of it", I(LGXF_APN),
1424 "Ask the user every time", I(LGXF_ASK), NULL);
1425 ctrl_checkbox(s, "Flush log file frequently", 'u',
1426 HELPCTX(logging_flush),
1427 conf_checkbox_handler, I(CONF_logflush));
1429 if ((midsession && protocol == PROT_SSH) ||
1430 (!midsession && backend_from_proto(PROT_SSH))) {
1431 s = ctrl_getset(b, "Session/Logging", "ssh",
1432 "Options specific to SSH packet logging");
1433 ctrl_checkbox(s, "Omit known password fields", 'k',
1434 HELPCTX(logging_ssh_omit_password),
1435 conf_checkbox_handler, I(CONF_logomitpass));
1436 ctrl_checkbox(s, "Omit session data", 'd',
1437 HELPCTX(logging_ssh_omit_data),
1438 conf_checkbox_handler, I(CONF_logomitdata));
1442 * The Terminal panel.
1444 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1446 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1447 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1448 HELPCTX(terminal_autowrap),
1449 conf_checkbox_handler, I(CONF_wrap_mode));
1450 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1451 HELPCTX(terminal_decom),
1452 conf_checkbox_handler, I(CONF_dec_om));
1453 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1454 HELPCTX(terminal_lfhascr),
1455 conf_checkbox_handler, I(CONF_lfhascr));
1456 ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1457 HELPCTX(terminal_crhaslf),
1458 conf_checkbox_handler, I(CONF_crhaslf));
1459 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1460 HELPCTX(terminal_bce),
1461 conf_checkbox_handler, I(CONF_bce));
1462 ctrl_checkbox(s, "Enable blinking text", 'n',
1463 HELPCTX(terminal_blink),
1464 conf_checkbox_handler, I(CONF_blinktext));
1465 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1466 HELPCTX(terminal_answerback),
1467 conf_editbox_handler, I(CONF_answerback), I(1));
1469 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1470 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1471 HELPCTX(terminal_localecho),
1472 conf_radiobutton_handler,I(CONF_localecho),
1474 "Force on", I(FORCE_ON),
1475 "Force off", I(FORCE_OFF), NULL);
1476 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1477 HELPCTX(terminal_localedit),
1478 conf_radiobutton_handler,I(CONF_localedit),
1480 "Force on", I(FORCE_ON),
1481 "Force off", I(FORCE_OFF), NULL);
1483 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1484 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1485 HELPCTX(terminal_printing),
1486 printerbox_handler, P(NULL), P(NULL));
1489 * The Terminal/Keyboard panel.
1491 ctrl_settitle(b, "Terminal/Keyboard",
1492 "Options controlling the effects of keys");
1494 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1495 "Change the sequences sent by:");
1496 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1497 HELPCTX(keyboard_backspace),
1498 conf_radiobutton_handler,
1499 I(CONF_bksp_is_delete),
1500 "Control-H", I(0), "Control-? (127)", I(1), NULL);
1501 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1502 HELPCTX(keyboard_homeend),
1503 conf_radiobutton_handler,
1504 I(CONF_rxvt_homeend),
1505 "Standard", I(0), "rxvt", I(1), NULL);
1506 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1507 HELPCTX(keyboard_funkeys),
1508 conf_radiobutton_handler,
1510 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1511 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1513 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1514 "Application keypad settings:");
1515 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1516 HELPCTX(keyboard_appcursor),
1517 conf_radiobutton_handler,
1519 "Normal", I(0), "Application", I(1), NULL);
1520 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1521 HELPCTX(keyboard_appkeypad),
1522 numeric_keypad_handler, P(NULL),
1523 "Normal", I(0), "Application", I(1), "NetHack", I(2),
1527 * The Terminal/Bell panel.
1529 ctrl_settitle(b, "Terminal/Bell",
1530 "Options controlling the terminal bell");
1532 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1533 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1534 HELPCTX(bell_style),
1535 conf_radiobutton_handler, I(CONF_beep),
1536 "None (bell disabled)", I(BELL_DISABLED),
1537 "Make default system alert sound", I(BELL_DEFAULT),
1538 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1540 s = ctrl_getset(b, "Terminal/Bell", "overload",
1541 "Control the bell overload behaviour");
1542 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1543 HELPCTX(bell_overload),
1544 conf_checkbox_handler, I(CONF_bellovl));
1545 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1546 HELPCTX(bell_overload),
1547 conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1548 ctrl_editbox(s, "... in this many seconds", 't', 20,
1549 HELPCTX(bell_overload),
1550 conf_editbox_handler, I(CONF_bellovl_t),
1552 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1553 HELPCTX(bell_overload));
1554 ctrl_editbox(s, "Seconds of silence required", 's', 20,
1555 HELPCTX(bell_overload),
1556 conf_editbox_handler, I(CONF_bellovl_s),
1560 * The Terminal/Features panel.
1562 ctrl_settitle(b, "Terminal/Features",
1563 "Enabling and disabling advanced terminal features");
1565 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1566 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1567 HELPCTX(features_application),
1568 conf_checkbox_handler, I(CONF_no_applic_c));
1569 ctrl_checkbox(s, "Disable application keypad mode", 'k',
1570 HELPCTX(features_application),
1571 conf_checkbox_handler, I(CONF_no_applic_k));
1572 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1573 HELPCTX(features_mouse),
1574 conf_checkbox_handler, I(CONF_no_mouse_rep));
1575 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1576 HELPCTX(features_resize),
1577 conf_checkbox_handler,
1578 I(CONF_no_remote_resize));
1579 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1580 HELPCTX(features_altscreen),
1581 conf_checkbox_handler, I(CONF_no_alt_screen));
1582 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1583 HELPCTX(features_retitle),
1584 conf_checkbox_handler,
1585 I(CONF_no_remote_wintitle));
1586 ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1587 HELPCTX(features_qtitle),
1588 conf_radiobutton_handler,
1589 I(CONF_remote_qtitle_action),
1590 "None", I(TITLE_NONE),
1591 "Empty string", I(TITLE_EMPTY),
1592 "Window title", I(TITLE_REAL), NULL);
1593 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1594 HELPCTX(features_dbackspace),
1595 conf_checkbox_handler, I(CONF_no_dbackspace));
1596 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1597 'r', HELPCTX(features_charset), conf_checkbox_handler,
1598 I(CONF_no_remote_charset));
1599 ctrl_checkbox(s, "Disable Arabic text shaping",
1600 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1601 I(CONF_arabicshaping));
1602 ctrl_checkbox(s, "Disable bidirectional text display",
1603 'd', HELPCTX(features_bidi), conf_checkbox_handler,
1609 str = dupprintf("Options controlling %s's window", appname);
1610 ctrl_settitle(b, "Window", str);
1613 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1614 ctrl_columns(s, 2, 50, 50);
1615 c = ctrl_editbox(s, "Columns", 'm', 100,
1616 HELPCTX(window_size),
1617 conf_editbox_handler, I(CONF_width), I(-1));
1618 c->generic.column = 0;
1619 c = ctrl_editbox(s, "Rows", 'r', 100,
1620 HELPCTX(window_size),
1621 conf_editbox_handler, I(CONF_height),I(-1));
1622 c->generic.column = 1;
1623 ctrl_columns(s, 1, 100);
1625 s = ctrl_getset(b, "Window", "scrollback",
1626 "Control the scrollback in the window");
1627 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1628 HELPCTX(window_scrollback),
1629 conf_editbox_handler, I(CONF_savelines), I(-1));
1630 ctrl_checkbox(s, "Display scrollbar", 'd',
1631 HELPCTX(window_scrollback),
1632 conf_checkbox_handler, I(CONF_scrollbar));
1633 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1634 HELPCTX(window_scrollback),
1635 conf_checkbox_handler, I(CONF_scroll_on_key));
1636 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1637 HELPCTX(window_scrollback),
1638 conf_checkbox_handler, I(CONF_scroll_on_disp));
1639 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1640 HELPCTX(window_erased),
1641 conf_checkbox_handler,
1642 I(CONF_erase_to_scrollback));
1645 * The Window/Appearance panel.
1647 str = dupprintf("Configure the appearance of %s's window", appname);
1648 ctrl_settitle(b, "Window/Appearance", str);
1651 s = ctrl_getset(b, "Window/Appearance", "cursor",
1652 "Adjust the use of the cursor");
1653 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1654 HELPCTX(appearance_cursor),
1655 conf_radiobutton_handler,
1656 I(CONF_cursor_type),
1658 "Underline", 'u', I(1),
1659 "Vertical line", 'v', I(2), NULL);
1660 ctrl_checkbox(s, "Cursor blinks", 'b',
1661 HELPCTX(appearance_cursor),
1662 conf_checkbox_handler, I(CONF_blink_cur));
1664 s = ctrl_getset(b, "Window/Appearance", "font",
1666 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1667 HELPCTX(appearance_font),
1668 conf_fontsel_handler, I(CONF_font));
1670 s = ctrl_getset(b, "Window/Appearance", "mouse",
1671 "Adjust the use of the mouse pointer");
1672 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1673 HELPCTX(appearance_hidemouse),
1674 conf_checkbox_handler, I(CONF_hide_mouseptr));
1676 s = ctrl_getset(b, "Window/Appearance", "border",
1677 "Adjust the window border");
1678 ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1679 HELPCTX(appearance_border),
1680 conf_editbox_handler,
1681 I(CONF_window_border), I(-1));
1684 * The Window/Behaviour panel.
1686 str = dupprintf("Configure the behaviour of %s's window", appname);
1687 ctrl_settitle(b, "Window/Behaviour", str);
1690 s = ctrl_getset(b, "Window/Behaviour", "title",
1691 "Adjust the behaviour of the window title");
1692 ctrl_editbox(s, "Window title:", 't', 100,
1693 HELPCTX(appearance_title),
1694 conf_editbox_handler, I(CONF_wintitle), I(1));
1695 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1696 HELPCTX(appearance_title),
1697 conf_checkbox_handler,
1698 I(CHECKBOX_INVERT | CONF_win_name_always));
1700 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1701 ctrl_checkbox(s, "Warn before closing window", 'w',
1702 HELPCTX(behaviour_closewarn),
1703 conf_checkbox_handler, I(CONF_warn_on_close));
1706 * The Window/Translation panel.
1708 ctrl_settitle(b, "Window/Translation",
1709 "Options controlling character set translation");
1711 s = ctrl_getset(b, "Window/Translation", "trans",
1712 "Character set translation");
1713 ctrl_combobox(s, "Remote character set:",
1714 'r', 100, HELPCTX(translation_codepage),
1715 codepage_handler, P(NULL), P(NULL));
1717 s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1718 ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1719 HELPCTX(translation_cjk_ambig_wide),
1720 conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1722 str = dupprintf("Adjust how %s handles line drawing characters", appname);
1723 s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1725 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1726 HELPCTX(translation_linedraw),
1727 conf_radiobutton_handler,
1729 "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1730 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1732 ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1733 HELPCTX(selection_linedraw),
1734 conf_checkbox_handler, I(CONF_rawcnp));
1737 * The Window/Selection panel.
1739 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1741 s = ctrl_getset(b, "Window/Selection", "mouse",
1742 "Control use of mouse");
1743 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1744 HELPCTX(selection_shiftdrag),
1745 conf_checkbox_handler, I(CONF_mouse_override));
1746 ctrl_radiobuttons(s,
1747 "Default selection mode (Alt+drag does the other one):",
1749 HELPCTX(selection_rect),
1750 conf_radiobutton_handler,
1751 I(CONF_rect_select),
1752 "Normal", 'n', I(0),
1753 "Rectangular block", 'r', I(1), NULL);
1755 s = ctrl_getset(b, "Window/Selection", "charclass",
1756 "Control the select-one-word-at-a-time mode");
1757 ccd = (struct charclass_data *)
1758 ctrl_alloc(b, sizeof(struct charclass_data));
1759 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1760 HELPCTX(selection_charclasses),
1761 charclass_handler, P(ccd));
1762 ccd->listbox->listbox.multisel = 1;
1763 ccd->listbox->listbox.ncols = 4;
1764 ccd->listbox->listbox.percentages = snewn(4, int);
1765 ccd->listbox->listbox.percentages[0] = 15;
1766 ccd->listbox->listbox.percentages[1] = 25;
1767 ccd->listbox->listbox.percentages[2] = 20;
1768 ccd->listbox->listbox.percentages[3] = 40;
1769 ctrl_columns(s, 2, 67, 33);
1770 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1771 HELPCTX(selection_charclasses),
1772 charclass_handler, P(ccd), P(NULL));
1773 ccd->editbox->generic.column = 0;
1774 ccd->button = ctrl_pushbutton(s, "Set", 's',
1775 HELPCTX(selection_charclasses),
1776 charclass_handler, P(ccd));
1777 ccd->button->generic.column = 1;
1778 ctrl_columns(s, 1, 100);
1781 * The Window/Colours panel.
1783 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1785 s = ctrl_getset(b, "Window/Colours", "general",
1786 "General options for colour usage");
1787 ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1788 HELPCTX(colours_ansi),
1789 conf_checkbox_handler, I(CONF_ansi_colour));
1790 ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1791 HELPCTX(colours_xterm256), conf_checkbox_handler,
1792 I(CONF_xterm_256_colour));
1793 ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1794 HELPCTX(colours_bold),
1795 conf_checkbox_handler, I(CONF_bold_colour));
1797 str = dupprintf("Adjust the precise colours %s displays", appname);
1798 s = ctrl_getset(b, "Window/Colours", "adjust", str);
1800 ctrl_text(s, "Select a colour from the list, and then click the"
1801 " Modify button to change its appearance.",
1802 HELPCTX(colours_config));
1803 ctrl_columns(s, 2, 67, 33);
1804 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1805 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1806 HELPCTX(colours_config), colour_handler, P(cd));
1807 cd->listbox->generic.column = 0;
1808 cd->listbox->listbox.height = 7;
1809 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1810 c->generic.column = 1;
1811 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1812 colour_handler, P(cd), P(NULL));
1813 cd->redit->generic.column = 1;
1814 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1815 colour_handler, P(cd), P(NULL));
1816 cd->gedit->generic.column = 1;
1817 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1818 colour_handler, P(cd), P(NULL));
1819 cd->bedit->generic.column = 1;
1820 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1821 colour_handler, P(cd));
1822 cd->button->generic.column = 1;
1823 ctrl_columns(s, 1, 100);
1826 * The Connection panel. This doesn't show up if we're in a
1827 * non-network utility such as pterm. We tell this by being
1828 * passed a protocol < 0.
1830 if (protocol >= 0) {
1831 ctrl_settitle(b, "Connection", "Options controlling the connection");
1833 s = ctrl_getset(b, "Connection", "keepalive",
1834 "Sending of null packets to keep session active");
1835 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1836 HELPCTX(connection_keepalive),
1837 conf_editbox_handler, I(CONF_ping_interval),
1841 s = ctrl_getset(b, "Connection", "tcp",
1842 "Low-level TCP connection options");
1843 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1844 'n', HELPCTX(connection_nodelay),
1845 conf_checkbox_handler,
1846 I(CONF_tcp_nodelay));
1847 ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1848 'p', HELPCTX(connection_tcpkeepalive),
1849 conf_checkbox_handler,
1850 I(CONF_tcp_keepalives));
1852 s = ctrl_getset(b, "Connection", "ipversion",
1853 "Internet protocol version");
1854 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1855 HELPCTX(connection_ipversion),
1856 conf_radiobutton_handler,
1857 I(CONF_addressfamily),
1858 "Auto", 'u', I(ADDRTYPE_UNSPEC),
1859 "IPv4", '4', I(ADDRTYPE_IPV4),
1860 "IPv6", '6', I(ADDRTYPE_IPV6),
1865 char *label = backend_from_proto(PROT_SSH) ?
1866 "Logical name of remote host (e.g. for SSH key lookup):" :
1867 "Logical name of remote host:";
1868 s = ctrl_getset(b, "Connection", "identity",
1869 "Logical name of remote host");
1870 ctrl_editbox(s, label, 'm', 100,
1871 HELPCTX(connection_loghost),
1872 conf_editbox_handler, I(CONF_loghost), I(1));
1877 * A sub-panel Connection/Data, containing options that
1878 * decide on data to send to the server.
1881 ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1883 s = ctrl_getset(b, "Connection/Data", "login",
1885 ctrl_editbox(s, "Auto-login username", 'u', 50,
1886 HELPCTX(connection_username),
1887 conf_editbox_handler, I(CONF_username), I(1));
1889 /* We assume the local username is sufficiently stable
1890 * to include on the dialog box. */
1891 char *user = get_username();
1892 char *userlabel = dupprintf("Use system username (%s)",
1895 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1896 HELPCTX(connection_username_from_env),
1897 conf_radiobutton_handler,
1898 I(CONF_username_from_env),
1905 s = ctrl_getset(b, "Connection/Data", "term",
1906 "Terminal details");
1907 ctrl_editbox(s, "Terminal-type string", 't', 50,
1908 HELPCTX(connection_termtype),
1909 conf_editbox_handler, I(CONF_termtype), I(1));
1910 ctrl_editbox(s, "Terminal speeds", 's', 50,
1911 HELPCTX(connection_termspeed),
1912 conf_editbox_handler, I(CONF_termspeed), I(1));
1914 s = ctrl_getset(b, "Connection/Data", "env",
1915 "Environment variables");
1916 ctrl_columns(s, 2, 80, 20);
1917 ed = (struct environ_data *)
1918 ctrl_alloc(b, sizeof(struct environ_data));
1919 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1920 HELPCTX(telnet_environ),
1921 environ_handler, P(ed), P(NULL));
1922 ed->varbox->generic.column = 0;
1923 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1924 HELPCTX(telnet_environ),
1925 environ_handler, P(ed), P(NULL));
1926 ed->valbox->generic.column = 0;
1927 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1928 HELPCTX(telnet_environ),
1929 environ_handler, P(ed));
1930 ed->addbutton->generic.column = 1;
1931 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1932 HELPCTX(telnet_environ),
1933 environ_handler, P(ed));
1934 ed->rembutton->generic.column = 1;
1935 ctrl_columns(s, 1, 100);
1936 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1937 HELPCTX(telnet_environ),
1938 environ_handler, P(ed));
1939 ed->listbox->listbox.height = 3;
1940 ed->listbox->listbox.ncols = 2;
1941 ed->listbox->listbox.percentages = snewn(2, int);
1942 ed->listbox->listbox.percentages[0] = 30;
1943 ed->listbox->listbox.percentages[1] = 70;
1950 * The Connection/Proxy panel.
1952 ctrl_settitle(b, "Connection/Proxy",
1953 "Options controlling proxy usage");
1955 s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1956 ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1957 HELPCTX(proxy_type),
1958 conf_radiobutton_handler,
1960 "None", I(PROXY_NONE),
1961 "SOCKS 4", I(PROXY_SOCKS4),
1962 "SOCKS 5", I(PROXY_SOCKS5),
1963 "HTTP", I(PROXY_HTTP),
1964 "Telnet", I(PROXY_TELNET),
1966 ctrl_columns(s, 2, 80, 20);
1967 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1968 HELPCTX(proxy_main),
1969 conf_editbox_handler,
1970 I(CONF_proxy_host), I(1));
1971 c->generic.column = 0;
1972 c = ctrl_editbox(s, "Port", 'p', 100,
1973 HELPCTX(proxy_main),
1974 conf_editbox_handler,
1977 c->generic.column = 1;
1978 ctrl_columns(s, 1, 100);
1979 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1980 HELPCTX(proxy_exclude),
1981 conf_editbox_handler,
1982 I(CONF_proxy_exclude_list), I(1));
1983 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1984 HELPCTX(proxy_exclude),
1985 conf_checkbox_handler,
1986 I(CONF_even_proxy_localhost));
1987 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1989 conf_radiobutton_handler,
1993 "Yes", I(FORCE_ON), NULL);
1994 ctrl_editbox(s, "Username", 'u', 60,
1995 HELPCTX(proxy_auth),
1996 conf_editbox_handler,
1997 I(CONF_proxy_username), I(1));
1998 c = ctrl_editbox(s, "Password", 'w', 60,
1999 HELPCTX(proxy_auth),
2000 conf_editbox_handler,
2001 I(CONF_proxy_password), I(1));
2002 c->editbox.password = 1;
2003 ctrl_editbox(s, "Telnet command", 'm', 100,
2004 HELPCTX(proxy_command),
2005 conf_editbox_handler,
2006 I(CONF_proxy_telnet_command), I(1));
2010 * The Telnet panel exists in the base config box, and in a
2011 * mid-session reconfig box _if_ we're using Telnet.
2013 if (!midsession || protocol == PROT_TELNET) {
2015 * The Connection/Telnet panel.
2017 ctrl_settitle(b, "Connection/Telnet",
2018 "Options controlling Telnet connections");
2020 s = ctrl_getset(b, "Connection/Telnet", "protocol",
2021 "Telnet protocol adjustments");
2024 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2026 HELPCTX(telnet_oldenviron),
2027 conf_radiobutton_handler,
2028 I(CONF_rfc_environ),
2029 "BSD (commonplace)", 'b', I(0),
2030 "RFC 1408 (unusual)", 'f', I(1), NULL);
2031 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2032 HELPCTX(telnet_passive),
2033 conf_radiobutton_handler,
2034 I(CONF_passive_telnet),
2035 "Passive", I(1), "Active", I(0), NULL);
2037 ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2038 HELPCTX(telnet_specialkeys),
2039 conf_checkbox_handler,
2040 I(CONF_telnet_keyboard));
2041 ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2042 'm', HELPCTX(telnet_newline),
2043 conf_checkbox_handler,
2044 I(CONF_telnet_newline));
2050 * The Connection/Rlogin panel.
2052 ctrl_settitle(b, "Connection/Rlogin",
2053 "Options controlling Rlogin connections");
2055 s = ctrl_getset(b, "Connection/Rlogin", "data",
2056 "Data to send to the server");
2057 ctrl_editbox(s, "Local username:", 'l', 50,
2058 HELPCTX(rlogin_localuser),
2059 conf_editbox_handler, I(CONF_localusername), I(1));
2064 * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2065 * when we're not doing SSH.
2068 if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2071 * The Connection/SSH panel.
2073 ctrl_settitle(b, "Connection/SSH",
2074 "Options controlling SSH connections");
2076 if (midsession && protcfginfo == 1) {
2077 s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2078 ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2079 "session; it is only here so that sub-panels of it can "
2080 "exist without looking strange.", HELPCTX(no_help));
2085 s = ctrl_getset(b, "Connection/SSH", "data",
2086 "Data to send to the server");
2087 ctrl_editbox(s, "Remote command:", 'r', 100,
2088 HELPCTX(ssh_command),
2089 conf_editbox_handler, I(CONF_remote_cmd), I(1));
2091 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2092 ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2093 HELPCTX(ssh_noshell),
2094 conf_checkbox_handler,
2095 I(CONF_ssh_no_shell));
2098 if (!midsession || protcfginfo != 1) {
2099 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2101 ctrl_checkbox(s, "Enable compression", 'e',
2102 HELPCTX(ssh_compress),
2103 conf_checkbox_handler,
2104 I(CONF_compression));
2108 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2110 ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2111 HELPCTX(ssh_protocol),
2112 conf_radiobutton_handler,
2114 "1 only", 'l', I(0),
2117 "2 only", 'y', I(3), NULL);
2120 if (!midsession || protcfginfo != 1) {
2121 s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
2122 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2123 HELPCTX(ssh_ciphers),
2124 cipherlist_handler, P(NULL));
2125 c->listbox.height = 6;
2127 ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2128 HELPCTX(ssh_ciphers),
2129 conf_checkbox_handler,
2130 I(CONF_ssh2_des_cbc));
2134 * The Connection/SSH/Kex panel. (Owing to repeat key
2135 * exchange, this is all meaningful in mid-session _if_
2136 * we're using SSH-2 or haven't decided yet.)
2138 if (protcfginfo != 1) {
2139 ctrl_settitle(b, "Connection/SSH/Kex",
2140 "Options controlling SSH key exchange");
2142 s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2143 "Key exchange algorithm options");
2144 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2145 HELPCTX(ssh_kexlist),
2146 kexlist_handler, P(NULL));
2147 c->listbox.height = 5;
2149 s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2150 "Options controlling key re-exchange");
2152 ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2153 HELPCTX(ssh_kex_repeat),
2154 conf_editbox_handler,
2155 I(CONF_ssh_rekey_time),
2157 ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2158 HELPCTX(ssh_kex_repeat),
2159 conf_editbox_handler,
2160 I(CONF_ssh_rekey_data),
2162 ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2163 HELPCTX(ssh_kex_repeat));
2169 * The Connection/SSH/Auth panel.
2171 ctrl_settitle(b, "Connection/SSH/Auth",
2172 "Options controlling SSH authentication");
2174 s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2175 ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2176 HELPCTX(ssh_auth_bypass),
2177 conf_checkbox_handler,
2178 I(CONF_ssh_no_userauth));
2179 ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2180 'd', HELPCTX(ssh_auth_banner),
2181 conf_checkbox_handler,
2182 I(CONF_ssh_show_banner));
2184 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2185 "Authentication methods");
2186 ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2187 HELPCTX(ssh_auth_pageant),
2188 conf_checkbox_handler,
2190 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2191 HELPCTX(ssh_auth_tis),
2192 conf_checkbox_handler,
2193 I(CONF_try_tis_auth));
2194 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2195 'i', HELPCTX(ssh_auth_ki),
2196 conf_checkbox_handler,
2197 I(CONF_try_ki_auth));
2199 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2200 "Authentication parameters");
2201 ctrl_checkbox(s, "Allow agent forwarding", 'f',
2202 HELPCTX(ssh_auth_agentfwd),
2203 conf_checkbox_handler, I(CONF_agentfwd));
2204 ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2205 HELPCTX(ssh_auth_changeuser),
2206 conf_checkbox_handler,
2207 I(CONF_change_username));
2208 ctrl_filesel(s, "Private key file for authentication:", 'k',
2209 FILTER_KEY_FILES, FALSE, "Select private key file",
2210 HELPCTX(ssh_auth_privkey),
2211 conf_filesel_handler, I(CONF_keyfile));
2215 * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2216 * the main Auth panel.
2218 ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2219 "Options controlling GSSAPI authentication");
2220 s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2222 ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2223 't', HELPCTX(ssh_gssapi),
2224 conf_checkbox_handler,
2225 I(CONF_try_gssapi_auth));
2227 ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2228 HELPCTX(ssh_gssapi_delegation),
2229 conf_checkbox_handler,
2233 * GSSAPI library selection.
2236 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2237 'p', HELPCTX(ssh_gssapi_libraries),
2238 gsslist_handler, P(NULL));
2239 c->listbox.height = ngsslibs;
2242 * I currently assume that if more than one GSS
2243 * library option is available, then one of them is
2244 * 'user-supplied' and so we should present the
2245 * following file selector. This is at least half-
2246 * reasonable, because if we're using statically
2247 * linked GSSAPI then there will only be one option
2248 * and no way to load from a user-supplied library,
2249 * whereas if we're using dynamic libraries then
2250 * there will almost certainly be some default
2251 * option in addition to a user-supplied path. If
2252 * anyone ever ports PuTTY to a system on which
2253 * dynamic-library GSSAPI is available but there is
2254 * absolutely no consensus on where to keep the
2255 * libraries, there'll need to be a flag alongside
2256 * ngsslibs to control whether the file selector is
2260 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2261 FILTER_DYNLIB_FILES, FALSE, "Select library file",
2262 HELPCTX(ssh_gssapi_libraries),
2263 conf_filesel_handler,
2264 I(CONF_ssh_gss_custom));
2271 * The Connection/SSH/TTY panel.
2273 ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2275 s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2276 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2278 conf_checkbox_handler,
2281 s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2283 td = (struct ttymodes_data *)
2284 ctrl_alloc(b, sizeof(struct ttymodes_data));
2285 ctrl_columns(s, 2, 75, 25);
2286 c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2287 c->generic.column = 0;
2288 td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2289 HELPCTX(ssh_ttymodes),
2290 ttymodes_handler, P(td));
2291 td->rembutton->generic.column = 1;
2292 td->rembutton->generic.tabdelay = 1;
2293 ctrl_columns(s, 1, 100);
2294 td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2295 HELPCTX(ssh_ttymodes),
2296 ttymodes_handler, P(td));
2297 td->listbox->listbox.multisel = 1;
2298 td->listbox->listbox.height = 4;
2299 td->listbox->listbox.ncols = 2;
2300 td->listbox->listbox.percentages = snewn(2, int);
2301 td->listbox->listbox.percentages[0] = 40;
2302 td->listbox->listbox.percentages[1] = 60;
2303 ctrl_tabdelay(s, td->rembutton);
2304 ctrl_columns(s, 2, 75, 25);
2305 td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2306 HELPCTX(ssh_ttymodes),
2307 ttymodes_handler, P(td));
2308 td->modelist->generic.column = 0;
2309 td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2310 HELPCTX(ssh_ttymodes),
2311 ttymodes_handler, P(td));
2312 td->addbutton->generic.column = 1;
2313 td->addbutton->generic.tabdelay = 1;
2314 ctrl_columns(s, 1, 100); /* column break */
2315 /* Bit of a hack to get the value radio buttons and
2316 * edit-box on the same row. */
2317 ctrl_columns(s, 3, 25, 50, 25);
2318 c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2319 c->generic.column = 0;
2320 td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2321 HELPCTX(ssh_ttymodes),
2322 ttymodes_handler, P(td),
2323 "Auto", NO_SHORTCUT, P(NULL),
2324 "This:", NO_SHORTCUT, P(NULL),
2326 td->valradio->generic.column = 1;
2327 td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2328 HELPCTX(ssh_ttymodes),
2329 ttymodes_handler, P(td), P(NULL));
2330 td->valbox->generic.column = 2;
2331 ctrl_tabdelay(s, td->addbutton);
2337 * The Connection/SSH/X11 panel.
2339 ctrl_settitle(b, "Connection/SSH/X11",
2340 "Options controlling SSH X11 forwarding");
2342 s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2343 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2344 HELPCTX(ssh_tunnels_x11),
2345 conf_checkbox_handler,I(CONF_x11_forward));
2346 ctrl_editbox(s, "X display location", 'x', 50,
2347 HELPCTX(ssh_tunnels_x11),
2348 conf_editbox_handler, I(CONF_x11_display), I(1));
2349 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2350 HELPCTX(ssh_tunnels_x11auth),
2351 conf_radiobutton_handler,
2353 "MIT-Magic-Cookie-1", I(X11_MIT),
2354 "XDM-Authorization-1", I(X11_XDM), NULL);
2358 * The Tunnels panel _is_ still available in mid-session.
2360 ctrl_settitle(b, "Connection/SSH/Tunnels",
2361 "Options controlling SSH port forwarding");
2363 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2365 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2366 HELPCTX(ssh_tunnels_portfwd_localhost),
2367 conf_checkbox_handler,
2368 I(CONF_lport_acceptall));
2369 ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2370 HELPCTX(ssh_tunnels_portfwd_localhost),
2371 conf_checkbox_handler,
2372 I(CONF_rport_acceptall));
2374 ctrl_columns(s, 3, 55, 20, 25);
2375 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2376 c->generic.column = COLUMN_FIELD(0,2);
2377 /* You want to select from the list, _then_ hit Remove. So tab order
2378 * should be that way round. */
2379 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2380 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2381 HELPCTX(ssh_tunnels_portfwd),
2382 portfwd_handler, P(pfd));
2383 pfd->rembutton->generic.column = 2;
2384 pfd->rembutton->generic.tabdelay = 1;
2385 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2386 HELPCTX(ssh_tunnels_portfwd),
2387 portfwd_handler, P(pfd));
2388 pfd->listbox->listbox.height = 3;
2389 pfd->listbox->listbox.ncols = 2;
2390 pfd->listbox->listbox.percentages = snewn(2, int);
2391 pfd->listbox->listbox.percentages[0] = 20;
2392 pfd->listbox->listbox.percentages[1] = 80;
2393 ctrl_tabdelay(s, pfd->rembutton);
2394 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2395 /* You want to enter source, destination and type, _then_ hit Add.
2396 * Again, we adjust the tab order to reflect this. */
2397 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2398 HELPCTX(ssh_tunnels_portfwd),
2399 portfwd_handler, P(pfd));
2400 pfd->addbutton->generic.column = 2;
2401 pfd->addbutton->generic.tabdelay = 1;
2402 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2403 HELPCTX(ssh_tunnels_portfwd),
2404 portfwd_handler, P(pfd), P(NULL));
2405 pfd->sourcebox->generic.column = 0;
2406 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2407 HELPCTX(ssh_tunnels_portfwd),
2408 portfwd_handler, P(pfd), P(NULL));
2409 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2410 HELPCTX(ssh_tunnels_portfwd),
2411 portfwd_handler, P(pfd),
2412 "Local", 'l', P(NULL),
2413 "Remote", 'm', P(NULL),
2414 "Dynamic", 'y', P(NULL),
2417 pfd->addressfamily =
2418 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2419 HELPCTX(ssh_tunnels_portfwd_ipversion),
2420 portfwd_handler, P(pfd),
2421 "Auto", 'u', I(ADDRTYPE_UNSPEC),
2422 "IPv4", '4', I(ADDRTYPE_IPV4),
2423 "IPv6", '6', I(ADDRTYPE_IPV6),
2426 ctrl_tabdelay(s, pfd->addbutton);
2427 ctrl_columns(s, 1, 100);
2431 * The Connection/SSH/Bugs panel.
2433 ctrl_settitle(b, "Connection/SSH/Bugs",
2434 "Workarounds for SSH server bugs");
2436 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2437 "Detection of known bugs in SSH servers");
2438 ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2439 HELPCTX(ssh_bugs_ignore1),
2440 sshbug_handler, I(CONF_sshbug_ignore1));
2441 ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2442 HELPCTX(ssh_bugs_plainpw1),
2443 sshbug_handler, I(CONF_sshbug_plainpw1));
2444 ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2445 HELPCTX(ssh_bugs_rsa1),
2446 sshbug_handler, I(CONF_sshbug_rsa1));
2447 ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2448 HELPCTX(ssh_bugs_ignore2),
2449 sshbug_handler, I(CONF_sshbug_ignore2));
2450 ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2451 HELPCTX(ssh_bugs_hmac2),
2452 sshbug_handler, I(CONF_sshbug_hmac2));
2453 ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2454 HELPCTX(ssh_bugs_derivekey2),
2455 sshbug_handler, I(CONF_sshbug_derivekey2));
2456 ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2457 HELPCTX(ssh_bugs_rsapad2),
2458 sshbug_handler, I(CONF_sshbug_rsapad2));
2459 ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2460 HELPCTX(ssh_bugs_pksessid2),
2461 sshbug_handler, I(CONF_sshbug_pksessid2));
2462 ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2463 HELPCTX(ssh_bugs_rekey2),
2464 sshbug_handler, I(CONF_sshbug_rekey2));
2465 ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2466 HELPCTX(ssh_bugs_maxpkt2),
2467 sshbug_handler, I(CONF_sshbug_maxpkt2));