Post-release destabilisation! Completely remove the struct type
[u/mdw/putty] / config.c
1 /*
2 * config.c - the platform-independent parts of the PuTTY
3 * configuration box.
4 */
5
6 #include <assert.h>
7 #include <stdlib.h>
8
9 #include "putty.h"
10 #include "dialog.h"
11 #include "storage.h"
12
13 #define PRINTER_DISABLED_STRING "None (printing disabled)"
14
15 #define HOST_BOX_TITLE "Host Name (or IP address)"
16 #define PORT_BOX_TITLE "Port"
17
18 void conf_radiobutton_handler(union control *ctrl, void *dlg,
19 void *data, int event)
20 {
21 int button;
22 Conf *conf = (Conf *)data;
23
24 /*
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.
29 */
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)
34 break;
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);
43 }
44 }
45
46 #define CHECKBOX_INVERT (1<<30)
47 void conf_checkbox_handler(union control *ctrl, void *dlg,
48 void *data, int event)
49 {
50 int key, invert;
51 Conf *conf = (Conf *)data;
52
53 /*
54 * For a standard checkbox, the context parameter gives the
55 * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
56 */
57 key = ctrl->checkbox.context.i;
58 if (key & CHECKBOX_INVERT) {
59 key &= ~CHECKBOX_INVERT;
60 invert = 1;
61 } else
62 invert = 0;
63
64 /*
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.)
68 */
69
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);
75 }
76 }
77
78 void conf_editbox_handler(union control *ctrl, void *dlg,
79 void *data, int event)
80 {
81 /*
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:
85 *
86 * - if context2 > 0, the field is a string.
87 * - if context2 == -1, the field is an int and the edit box
88 * is numeric.
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
92 * the field to 1200.)
93 */
94 int key = ctrl->editbox.context.i;
95 int length = ctrl->editbox.context2.i;
96 Conf *conf = (Conf *)data;
97
98 if (length > 0) {
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);
105 sfree(field);
106 }
107 } else if (length < 0) {
108 if (event == EVENT_REFRESH) {
109 char str[80];
110 int value = conf_get_int(conf, key);
111 if (length == -1)
112 sprintf(str, "%d", value);
113 else
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);
118 if (length == -1)
119 conf_set_int(conf, key, atoi(str));
120 else
121 conf_set_int(conf, key, (int)((-length) * atof(str)));
122 sfree(str);
123 }
124 }
125 }
126
127 void conf_filesel_handler(union control *ctrl, void *dlg,
128 void *data, int event)
129 {
130 int key = ctrl->fileselect.context.i;
131 Conf *conf = (Conf *)data;
132
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;
137 dlg_filesel_get(ctrl, dlg, &filename);
138 conf_set_filename(conf, key, &filename);
139 /* If Filenames ever become dynamic, free this one. */
140 }
141 }
142
143 void conf_fontsel_handler(union control *ctrl, void *dlg,
144 void *data, int event)
145 {
146 int key = ctrl->fontselect.context.i;
147 Conf *conf = (Conf *)data;
148
149 if (event == EVENT_REFRESH) {
150 dlg_fontsel_set(ctrl, dlg, *conf_get_fontspec(conf, key));
151 } else if (event == EVENT_VALCHANGE) {
152 FontSpec fontspec;
153 dlg_fontsel_get(ctrl, dlg, &fontspec);
154 conf_set_fontspec(conf, key, &fontspec);
155 /* If FontSpecs ever become dynamic, free this one. */
156 }
157 }
158
159 static void config_host_handler(union control *ctrl, void *dlg,
160 void *data, int event)
161 {
162 Conf *conf = (Conf *)data;
163
164 /*
165 * This function works just like the standard edit box handler,
166 * only it has to choose the control's label and text from two
167 * different places depending on the protocol.
168 */
169 if (event == EVENT_REFRESH) {
170 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
171 /*
172 * This label text is carefully chosen to contain an n,
173 * since that's the shortcut for the host name control.
174 */
175 dlg_label_change(ctrl, dlg, "Serial line");
176 dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
177 } else {
178 dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
179 dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
180 }
181 } else if (event == EVENT_VALCHANGE) {
182 char *s = dlg_editbox_get(ctrl, dlg);
183 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
184 conf_set_str(conf, CONF_serline, s);
185 else
186 conf_set_str(conf, CONF_host, s);
187 sfree(s);
188 }
189 }
190
191 static void config_port_handler(union control *ctrl, void *dlg,
192 void *data, int event)
193 {
194 Conf *conf = (Conf *)data;
195 char buf[80];
196
197 /*
198 * This function works similarly to the standard edit box handler,
199 * only it has to choose the control's label and text from two
200 * different places depending on the protocol.
201 */
202 if (event == EVENT_REFRESH) {
203 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
204 /*
205 * This label text is carefully chosen to contain a p,
206 * since that's the shortcut for the port control.
207 */
208 dlg_label_change(ctrl, dlg, "Speed");
209 sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
210 } else {
211 dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
212 if (conf_get_int(conf, CONF_port) != 0)
213 sprintf(buf, "%d", conf_get_int(conf, CONF_port));
214 else
215 /* Display an (invalid) port of 0 as blank */
216 buf[0] = '\0';
217 }
218 dlg_editbox_set(ctrl, dlg, buf);
219 } else if (event == EVENT_VALCHANGE) {
220 char *s = dlg_editbox_get(ctrl, dlg);
221 int i = atoi(s);
222 sfree(s);
223
224 if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
225 conf_set_int(conf, CONF_serspeed, i);
226 else
227 conf_set_int(conf, CONF_port, i);
228 }
229 }
230
231 struct hostport {
232 union control *host, *port;
233 };
234
235 /*
236 * We export this function so that platform-specific config
237 * routines can use it to conveniently identify the protocol radio
238 * buttons in order to add to them.
239 */
240 void config_protocolbuttons_handler(union control *ctrl, void *dlg,
241 void *data, int event)
242 {
243 int button;
244 Conf *conf = (Conf *)data;
245 struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
246
247 /*
248 * This function works just like the standard radio-button
249 * handler, except that it also has to change the setting of
250 * the port box, and refresh both host and port boxes when. We
251 * expect the context parameter to point at a hostport
252 * structure giving the `union control's for both.
253 */
254 if (event == EVENT_REFRESH) {
255 int protocol = conf_get_int(conf, CONF_protocol);
256 for (button = 0; button < ctrl->radio.nbuttons; button++)
257 if (protocol == ctrl->radio.buttondata[button].i)
258 break;
259 /* We expected that `break' to happen, in all circumstances. */
260 assert(button < ctrl->radio.nbuttons);
261 dlg_radiobutton_set(ctrl, dlg, button);
262 } else if (event == EVENT_VALCHANGE) {
263 int oldproto = conf_get_int(conf, CONF_protocol);
264 int newproto, port;
265
266 button = dlg_radiobutton_get(ctrl, dlg);
267 assert(button >= 0 && button < ctrl->radio.nbuttons);
268 newproto = ctrl->radio.buttondata[button].i;
269 conf_set_int(conf, CONF_protocol, newproto);
270
271 if (oldproto != newproto) {
272 Backend *ob = backend_from_proto(oldproto);
273 Backend *nb = backend_from_proto(newproto);
274 assert(ob);
275 assert(nb);
276 /* Iff the user hasn't changed the port from the old protocol's
277 * default, update it with the new protocol's default.
278 * (This includes a "default" of 0, implying that there is no
279 * sensible default for that protocol; in this case it's
280 * displayed as a blank.)
281 * This helps with the common case of tabbing through the
282 * controls in order and setting a non-default port before
283 * getting to the protocol; we want that non-default port
284 * to be preserved. */
285 port = conf_get_int(conf, CONF_port);
286 if (port == ob->default_port)
287 conf_set_int(conf, CONF_port, nb->default_port);
288 }
289 dlg_refresh(hp->host, dlg);
290 dlg_refresh(hp->port, dlg);
291 }
292 }
293
294 static void loggingbuttons_handler(union control *ctrl, void *dlg,
295 void *data, int event)
296 {
297 int button;
298 Conf *conf = (Conf *)data;
299 /* This function works just like the standard radio-button handler,
300 * but it has to fall back to "no logging" in situations where the
301 * configured logging type isn't applicable.
302 */
303 if (event == EVENT_REFRESH) {
304 int logtype = conf_get_int(conf, CONF_logtype);
305
306 for (button = 0; button < ctrl->radio.nbuttons; button++)
307 if (logtype == ctrl->radio.buttondata[button].i)
308 break;
309
310 /* We fell off the end, so we lack the configured logging type */
311 if (button == ctrl->radio.nbuttons) {
312 button = 0;
313 conf_set_int(conf, CONF_logtype, LGTYP_NONE);
314 }
315 dlg_radiobutton_set(ctrl, dlg, button);
316 } else if (event == EVENT_VALCHANGE) {
317 button = dlg_radiobutton_get(ctrl, dlg);
318 assert(button >= 0 && button < ctrl->radio.nbuttons);
319 conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
320 }
321 }
322
323 static void numeric_keypad_handler(union control *ctrl, void *dlg,
324 void *data, int event)
325 {
326 int button;
327 Conf *conf = (Conf *)data;
328 /*
329 * This function works much like the standard radio button
330 * handler, but it has to handle two fields in Conf.
331 */
332 if (event == EVENT_REFRESH) {
333 if (conf_get_int(conf, CONF_nethack_keypad))
334 button = 2;
335 else if (conf_get_int(conf, CONF_app_keypad))
336 button = 1;
337 else
338 button = 0;
339 assert(button < ctrl->radio.nbuttons);
340 dlg_radiobutton_set(ctrl, dlg, button);
341 } else if (event == EVENT_VALCHANGE) {
342 button = dlg_radiobutton_get(ctrl, dlg);
343 assert(button >= 0 && button < ctrl->radio.nbuttons);
344 if (button == 2) {
345 conf_set_int(conf, CONF_app_keypad, FALSE);
346 conf_set_int(conf, CONF_nethack_keypad, TRUE);
347 } else {
348 conf_set_int(conf, CONF_app_keypad, (button != 0));
349 conf_set_int(conf, CONF_nethack_keypad, FALSE);
350 }
351 }
352 }
353
354 static void cipherlist_handler(union control *ctrl, void *dlg,
355 void *data, int event)
356 {
357 Conf *conf = (Conf *)data;
358 if (event == EVENT_REFRESH) {
359 int i;
360
361 static const struct { char *s; int c; } ciphers[] = {
362 { "3DES", CIPHER_3DES },
363 { "Blowfish", CIPHER_BLOWFISH },
364 { "DES", CIPHER_DES },
365 { "AES (SSH-2 only)", CIPHER_AES },
366 { "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
367 { "-- warn below here --", CIPHER_WARN }
368 };
369
370 /* Set up the "selected ciphers" box. */
371 /* (cipherlist assumed to contain all ciphers) */
372 dlg_update_start(ctrl, dlg);
373 dlg_listbox_clear(ctrl, dlg);
374 for (i = 0; i < CIPHER_MAX; i++) {
375 int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
376 int j;
377 char *cstr = NULL;
378 for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
379 if (ciphers[j].c == c) {
380 cstr = ciphers[j].s;
381 break;
382 }
383 }
384 dlg_listbox_addwithid(ctrl, dlg, cstr, c);
385 }
386 dlg_update_done(ctrl, dlg);
387
388 } else if (event == EVENT_VALCHANGE) {
389 int i;
390
391 /* Update array to match the list box. */
392 for (i=0; i < CIPHER_MAX; i++)
393 conf_set_int_int(conf, CONF_ssh_cipherlist, i,
394 dlg_listbox_getid(ctrl, dlg, i));
395 }
396 }
397
398 #ifndef NO_GSSAPI
399 static void gsslist_handler(union control *ctrl, void *dlg,
400 void *data, int event)
401 {
402 Conf *conf = (Conf *)data;
403 if (event == EVENT_REFRESH) {
404 int i;
405
406 dlg_update_start(ctrl, dlg);
407 dlg_listbox_clear(ctrl, dlg);
408 for (i = 0; i < ngsslibs; i++) {
409 int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
410 assert(id >= 0 && id < ngsslibs);
411 dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
412 }
413 dlg_update_done(ctrl, dlg);
414
415 } else if (event == EVENT_VALCHANGE) {
416 int i;
417
418 /* Update array to match the list box. */
419 for (i=0; i < ngsslibs; i++)
420 conf_set_int_int(conf, CONF_ssh_gsslist, i,
421 dlg_listbox_getid(ctrl, dlg, i));
422 }
423 }
424 #endif
425
426 static void kexlist_handler(union control *ctrl, void *dlg,
427 void *data, int event)
428 {
429 Conf *conf = (Conf *)data;
430 if (event == EVENT_REFRESH) {
431 int i;
432
433 static const struct { char *s; int k; } kexes[] = {
434 { "Diffie-Hellman group 1", KEX_DHGROUP1 },
435 { "Diffie-Hellman group 14", KEX_DHGROUP14 },
436 { "Diffie-Hellman group exchange", KEX_DHGEX },
437 { "RSA-based key exchange", KEX_RSA },
438 { "-- warn below here --", KEX_WARN }
439 };
440
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);
447 int j;
448 char *kstr = NULL;
449 for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
450 if (kexes[j].k == k) {
451 kstr = kexes[j].s;
452 break;
453 }
454 }
455 dlg_listbox_addwithid(ctrl, dlg, kstr, k);
456 }
457 dlg_update_done(ctrl, dlg);
458
459 } else if (event == EVENT_VALCHANGE) {
460 int i;
461
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));
466 }
467 }
468
469 static void printerbox_handler(union control *ctrl, void *dlg,
470 void *data, int event)
471 {
472 Conf *conf = (Conf *)data;
473 if (event == EVENT_REFRESH) {
474 int nprinters, i;
475 printer_enum *pe;
476 char *printer;
477
478 dlg_update_start(ctrl, dlg);
479 /*
480 * Some backends may wish to disable the drop-down list on
481 * this edit box. Be prepared for this.
482 */
483 if (ctrl->editbox.has_list) {
484 dlg_listbox_clear(ctrl, dlg);
485 dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
486 pe = printer_start_enum(&nprinters);
487 for (i = 0; i < nprinters; i++)
488 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
489 printer_finish_enum(pe);
490 }
491 printer = conf_get_str(conf, CONF_printer);
492 if (!printer)
493 printer = PRINTER_DISABLED_STRING;
494 dlg_editbox_set(ctrl, dlg, printer);
495 dlg_update_done(ctrl, dlg);
496 } else if (event == EVENT_VALCHANGE) {
497 char *printer = dlg_editbox_get(ctrl, dlg);
498 if (!strcmp(printer, PRINTER_DISABLED_STRING))
499 printer[0] = '\0';
500 conf_set_str(conf, CONF_printer, printer);
501 sfree(printer);
502 }
503 }
504
505 static void codepage_handler(union control *ctrl, void *dlg,
506 void *data, int event)
507 {
508 Conf *conf = (Conf *)data;
509 if (event == EVENT_REFRESH) {
510 int i;
511 const char *cp, *thiscp;
512 dlg_update_start(ctrl, dlg);
513 thiscp = cp_name(decode_codepage(conf_get_str(conf,
514 CONF_line_codepage)));
515 dlg_listbox_clear(ctrl, dlg);
516 for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
517 dlg_listbox_add(ctrl, dlg, cp);
518 dlg_editbox_set(ctrl, dlg, thiscp);
519 conf_set_str(conf, CONF_line_codepage, thiscp);
520 dlg_update_done(ctrl, dlg);
521 } else if (event == EVENT_VALCHANGE) {
522 char *codepage = dlg_editbox_get(ctrl, dlg);
523 conf_set_str(conf, CONF_line_codepage,
524 cp_name(decode_codepage(codepage)));
525 sfree(codepage);
526 }
527 }
528
529 static void sshbug_handler(union control *ctrl, void *dlg,
530 void *data, int event)
531 {
532 Conf *conf = (Conf *)data;
533 if (event == EVENT_REFRESH) {
534 dlg_update_start(ctrl, dlg);
535 dlg_listbox_clear(ctrl, dlg);
536 dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
537 dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
538 dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
539 switch (conf_get_int(conf, ctrl->listbox.context.i)) {
540 case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
541 case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
542 case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
543 }
544 dlg_update_done(ctrl, dlg);
545 } else if (event == EVENT_SELCHANGE) {
546 int i = dlg_listbox_index(ctrl, dlg);
547 if (i < 0)
548 i = AUTO;
549 else
550 i = dlg_listbox_getid(ctrl, dlg, i);
551 conf_set_int(conf, ctrl->listbox.context.i, i);
552 }
553 }
554
555 #define SAVEDSESSION_LEN 2048
556
557 struct sessionsaver_data {
558 union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
559 union control *okbutton, *cancelbutton;
560 struct sesslist sesslist;
561 int midsession;
562 };
563
564 /*
565 * Helper function to load the session selected in the list box, if
566 * any, as this is done in more than one place below. Returns 0 for
567 * failure.
568 */
569 static int load_selected_session(struct sessionsaver_data *ssd,
570 char *savedsession,
571 void *dlg, Conf *conf, int *maybe_launch)
572 {
573 int i = dlg_listbox_index(ssd->listbox, dlg);
574 int isdef;
575 if (i < 0) {
576 dlg_beep(dlg);
577 return 0;
578 }
579 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
580 load_settings(ssd->sesslist.sessions[i], conf);
581 if (!isdef) {
582 strncpy(savedsession, ssd->sesslist.sessions[i],
583 SAVEDSESSION_LEN);
584 savedsession[SAVEDSESSION_LEN-1] = '\0';
585 if (maybe_launch)
586 *maybe_launch = TRUE;
587 } else {
588 savedsession[0] = '\0';
589 if (maybe_launch)
590 *maybe_launch = FALSE;
591 }
592 dlg_refresh(NULL, dlg);
593 /* Restore the selection, which might have been clobbered by
594 * changing the value of the edit box. */
595 dlg_listbox_select(ssd->listbox, dlg, i);
596 return 1;
597 }
598
599 static void sessionsaver_handler(union control *ctrl, void *dlg,
600 void *data, int event)
601 {
602 Conf *conf = (Conf *)data;
603 struct sessionsaver_data *ssd =
604 (struct sessionsaver_data *)ctrl->generic.context.p;
605 char *savedsession;
606
607 /*
608 * The first time we're called in a new dialog, we must
609 * allocate space to store the current contents of the saved
610 * session edit box (since it must persist even when we switch
611 * panels, but is not part of the Conf).
612 *
613 * FIXME: this is disgusting, and we'd do much better to have
614 * the persistent storage be dynamically allocated and get rid
615 * of the arbitrary limit SAVEDSESSION_LEN. To do that would
616 * require a means of making sure the memory gets freed at the
617 * appropriate moment.
618 */
619 if (!ssd->editbox) {
620 savedsession = NULL;
621 } else if (!dlg_get_privdata(ssd->editbox, dlg)) {
622 savedsession = (char *)
623 dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN);
624 savedsession[0] = '\0';
625 } else {
626 savedsession = dlg_get_privdata(ssd->editbox, dlg);
627 }
628
629 if (event == EVENT_REFRESH) {
630 if (ctrl == ssd->editbox) {
631 dlg_editbox_set(ctrl, dlg, savedsession);
632 } else if (ctrl == ssd->listbox) {
633 int i;
634 dlg_update_start(ctrl, dlg);
635 dlg_listbox_clear(ctrl, dlg);
636 for (i = 0; i < ssd->sesslist.nsessions; i++)
637 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
638 dlg_update_done(ctrl, dlg);
639 }
640 } else if (event == EVENT_VALCHANGE) {
641 int top, bottom, halfway, i;
642 if (ctrl == ssd->editbox) {
643 char *tmp = dlg_editbox_get(ctrl, dlg);
644 strncpy(savedsession, tmp, SAVEDSESSION_LEN);
645 sfree(tmp);
646 top = ssd->sesslist.nsessions;
647 bottom = -1;
648 while (top-bottom > 1) {
649 halfway = (top+bottom)/2;
650 i = strcmp(savedsession, ssd->sesslist.sessions[halfway]);
651 if (i <= 0 ) {
652 top = halfway;
653 } else {
654 bottom = halfway;
655 }
656 }
657 if (top == ssd->sesslist.nsessions) {
658 top -= 1;
659 }
660 dlg_listbox_select(ssd->listbox, dlg, top);
661 }
662 } else if (event == EVENT_ACTION) {
663 int mbl = FALSE;
664 if (!ssd->midsession &&
665 (ctrl == ssd->listbox ||
666 (ssd->loadbutton && ctrl == ssd->loadbutton))) {
667 /*
668 * The user has double-clicked a session, or hit Load.
669 * We must load the selected session, and then
670 * terminate the configuration dialog _if_ there was a
671 * double-click on the list box _and_ that session
672 * contains a hostname.
673 */
674 if (load_selected_session(ssd, savedsession, dlg, conf, &mbl) &&
675 (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
676 dlg_end(dlg, 1); /* it's all over, and succeeded */
677 }
678 } else if (ctrl == ssd->savebutton) {
679 int isdef = !strcmp(savedsession, "Default Settings");
680 if (!savedsession[0]) {
681 int i = dlg_listbox_index(ssd->listbox, dlg);
682 if (i < 0) {
683 dlg_beep(dlg);
684 return;
685 }
686 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
687 if (!isdef) {
688 strncpy(savedsession, ssd->sesslist.sessions[i],
689 SAVEDSESSION_LEN);
690 savedsession[SAVEDSESSION_LEN-1] = '\0';
691 } else {
692 savedsession[0] = '\0';
693 }
694 }
695 {
696 char *errmsg = save_settings(savedsession, conf);
697 if (errmsg) {
698 dlg_error_msg(dlg, errmsg);
699 sfree(errmsg);
700 }
701 }
702 get_sesslist(&ssd->sesslist, FALSE);
703 get_sesslist(&ssd->sesslist, TRUE);
704 dlg_refresh(ssd->editbox, dlg);
705 dlg_refresh(ssd->listbox, dlg);
706 } else if (!ssd->midsession &&
707 ssd->delbutton && ctrl == ssd->delbutton) {
708 int i = dlg_listbox_index(ssd->listbox, dlg);
709 if (i <= 0) {
710 dlg_beep(dlg);
711 } else {
712 del_settings(ssd->sesslist.sessions[i]);
713 get_sesslist(&ssd->sesslist, FALSE);
714 get_sesslist(&ssd->sesslist, TRUE);
715 dlg_refresh(ssd->listbox, dlg);
716 }
717 } else if (ctrl == ssd->okbutton) {
718 if (ssd->midsession) {
719 /* In a mid-session Change Settings, Apply is always OK. */
720 dlg_end(dlg, 1);
721 return;
722 }
723 /*
724 * Annoying special case. If the `Open' button is
725 * pressed while no host name is currently set, _and_
726 * the session list previously had the focus, _and_
727 * there was a session selected in that which had a
728 * valid host name in it, then load it and go.
729 */
730 if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
731 !conf_launchable(conf)) {
732 Conf *conf2 = conf_new();
733 int mbl = FALSE;
734 if (!load_selected_session(ssd, savedsession, dlg,
735 conf2, &mbl)) {
736 dlg_beep(dlg);
737 conf_free(conf2);
738 return;
739 }
740 /* If at this point we have a valid session, go! */
741 if (mbl && conf_launchable(conf2)) {
742 conf_copy_into(conf, conf2);
743 dlg_end(dlg, 1);
744 } else
745 dlg_beep(dlg);
746
747 conf_free(conf2);
748 return;
749 }
750
751 /*
752 * Otherwise, do the normal thing: if we have a valid
753 * session, get going.
754 */
755 if (conf_launchable(conf)) {
756 dlg_end(dlg, 1);
757 } else
758 dlg_beep(dlg);
759 } else if (ctrl == ssd->cancelbutton) {
760 dlg_end(dlg, 0);
761 }
762 }
763 }
764
765 struct charclass_data {
766 union control *listbox, *editbox, *button;
767 };
768
769 static void charclass_handler(union control *ctrl, void *dlg,
770 void *data, int event)
771 {
772 Conf *conf = (Conf *)data;
773 struct charclass_data *ccd =
774 (struct charclass_data *)ctrl->generic.context.p;
775
776 if (event == EVENT_REFRESH) {
777 if (ctrl == ccd->listbox) {
778 int i;
779 dlg_update_start(ctrl, dlg);
780 dlg_listbox_clear(ctrl, dlg);
781 for (i = 0; i < 128; i++) {
782 char str[100];
783 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
784 (i >= 0x21 && i != 0x7F) ? i : ' ',
785 conf_get_int_int(conf, CONF_wordness, i));
786 dlg_listbox_add(ctrl, dlg, str);
787 }
788 dlg_update_done(ctrl, dlg);
789 }
790 } else if (event == EVENT_ACTION) {
791 if (ctrl == ccd->button) {
792 char *str;
793 int i, n;
794 str = dlg_editbox_get(ccd->editbox, dlg);
795 n = atoi(str);
796 sfree(str);
797 for (i = 0; i < 128; i++) {
798 if (dlg_listbox_issel(ccd->listbox, dlg, i))
799 conf_set_int_int(conf, CONF_wordness, i, n);
800 }
801 dlg_refresh(ccd->listbox, dlg);
802 }
803 }
804 }
805
806 struct colour_data {
807 union control *listbox, *redit, *gedit, *bedit, *button;
808 };
809
810 static const char *const colours[] = {
811 "Default Foreground", "Default Bold Foreground",
812 "Default Background", "Default Bold Background",
813 "Cursor Text", "Cursor Colour",
814 "ANSI Black", "ANSI Black Bold",
815 "ANSI Red", "ANSI Red Bold",
816 "ANSI Green", "ANSI Green Bold",
817 "ANSI Yellow", "ANSI Yellow Bold",
818 "ANSI Blue", "ANSI Blue Bold",
819 "ANSI Magenta", "ANSI Magenta Bold",
820 "ANSI Cyan", "ANSI Cyan Bold",
821 "ANSI White", "ANSI White Bold"
822 };
823
824 static void colour_handler(union control *ctrl, void *dlg,
825 void *data, int event)
826 {
827 Conf *conf = (Conf *)data;
828 struct colour_data *cd =
829 (struct colour_data *)ctrl->generic.context.p;
830 int update = FALSE, clear = FALSE, r, g, b;
831
832 if (event == EVENT_REFRESH) {
833 if (ctrl == cd->listbox) {
834 int i;
835 dlg_update_start(ctrl, dlg);
836 dlg_listbox_clear(ctrl, dlg);
837 for (i = 0; i < lenof(colours); i++)
838 dlg_listbox_add(ctrl, dlg, colours[i]);
839 dlg_update_done(ctrl, dlg);
840 clear = TRUE;
841 update = TRUE;
842 }
843 } else if (event == EVENT_SELCHANGE) {
844 if (ctrl == cd->listbox) {
845 /* The user has selected a colour. Update the RGB text. */
846 int i = dlg_listbox_index(ctrl, dlg);
847 if (i < 0) {
848 clear = TRUE;
849 } else {
850 clear = FALSE;
851 r = conf_get_int_int(conf, CONF_colours, i*3+0);
852 g = conf_get_int_int(conf, CONF_colours, i*3+0);
853 b = conf_get_int_int(conf, CONF_colours, i*3+0);
854 }
855 update = TRUE;
856 }
857 } else if (event == EVENT_VALCHANGE) {
858 if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
859 /* The user has changed the colour using the edit boxes. */
860 char *str;
861 int i, cval;
862
863 str = dlg_editbox_get(ctrl, dlg);
864 cval = atoi(str);
865 sfree(str);
866 if (cval > 255) cval = 255;
867 if (cval < 0) cval = 0;
868
869 i = dlg_listbox_index(cd->listbox, dlg);
870 if (i >= 0) {
871 if (ctrl == cd->redit)
872 conf_set_int_int(conf, CONF_colours, i*3+0, cval);
873 else if (ctrl == cd->gedit)
874 conf_set_int_int(conf, CONF_colours, i*3+1, cval);
875 else if (ctrl == cd->bedit)
876 conf_set_int_int(conf, CONF_colours, i*3+2, cval);
877 }
878 }
879 } else if (event == EVENT_ACTION) {
880 if (ctrl == cd->button) {
881 int i = dlg_listbox_index(cd->listbox, dlg);
882 if (i < 0) {
883 dlg_beep(dlg);
884 return;
885 }
886 /*
887 * Start a colour selector, which will send us an
888 * EVENT_CALLBACK when it's finished and allow us to
889 * pick up the results.
890 */
891 dlg_coloursel_start(ctrl, dlg,
892 conf_get_int_int(conf, CONF_colours, i*3+0),
893 conf_get_int_int(conf, CONF_colours, i*3+1),
894 conf_get_int_int(conf, CONF_colours, i*3+2));
895 }
896 } else if (event == EVENT_CALLBACK) {
897 if (ctrl == cd->button) {
898 int i = dlg_listbox_index(cd->listbox, dlg);
899 /*
900 * Collect the results of the colour selector. Will
901 * return nonzero on success, or zero if the colour
902 * selector did nothing (user hit Cancel, for example).
903 */
904 if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
905 conf_set_int_int(conf, CONF_colours, i*3+0, r);
906 conf_set_int_int(conf, CONF_colours, i*3+0, g);
907 conf_set_int_int(conf, CONF_colours, i*3+0, b);
908 clear = FALSE;
909 update = TRUE;
910 }
911 }
912 }
913
914 if (update) {
915 if (clear) {
916 dlg_editbox_set(cd->redit, dlg, "");
917 dlg_editbox_set(cd->gedit, dlg, "");
918 dlg_editbox_set(cd->bedit, dlg, "");
919 } else {
920 char buf[40];
921 sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
922 sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
923 sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
924 }
925 }
926 }
927
928 struct ttymodes_data {
929 union control *modelist, *valradio, *valbox;
930 union control *addbutton, *rembutton, *listbox;
931 };
932
933 static void ttymodes_handler(union control *ctrl, void *dlg,
934 void *data, int event)
935 {
936 Conf *conf = (Conf *)data;
937 struct ttymodes_data *td =
938 (struct ttymodes_data *)ctrl->generic.context.p;
939
940 if (event == EVENT_REFRESH) {
941 if (ctrl == td->listbox) {
942 char *key, *val;
943 dlg_update_start(ctrl, dlg);
944 dlg_listbox_clear(ctrl, dlg);
945 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
946 val != NULL;
947 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
948 char *disp = dupprintf("%s\t%s", key,
949 (val[0] == 'A') ? "(auto)" : val+1);
950 dlg_listbox_add(ctrl, dlg, disp);
951 sfree(disp);
952 }
953 dlg_update_done(ctrl, dlg);
954 } else if (ctrl == td->modelist) {
955 int i;
956 dlg_update_start(ctrl, dlg);
957 dlg_listbox_clear(ctrl, dlg);
958 for (i = 0; ttymodes[i]; i++)
959 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
960 dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
961 dlg_update_done(ctrl, dlg);
962 } else if (ctrl == td->valradio) {
963 dlg_radiobutton_set(ctrl, dlg, 0);
964 }
965 } else if (event == EVENT_ACTION) {
966 if (ctrl == td->addbutton) {
967 int ind = dlg_listbox_index(td->modelist, dlg);
968 if (ind >= 0) {
969 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
970 const char *key;
971 char *str, *val;
972 /* Construct new entry */
973 key = ttymodes[ind];
974 str = dlg_editbox_get(td->valbox, dlg);
975 val = dupprintf("%c%s", type, str);
976 sfree(str);
977 conf_set_str_str(conf, CONF_ttymodes, key, val);
978 sfree(val);
979 dlg_refresh(td->listbox, dlg);
980 } else
981 dlg_beep(dlg);
982 } else if (ctrl == td->rembutton) {
983 int i = 0;
984 char *key, *val;
985 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
986 for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
987 val != NULL;
988 val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
989 if (dlg_listbox_issel(td->listbox, dlg, i)) {
990 if (!multisel) {
991 /* Populate controls with entry we're about to
992 * delete, for ease of editing.
993 * (If multiple entries were selected, don't
994 * touch the controls.) */
995 int ind = 0;
996 val++;
997 while (ttymodes[ind]) {
998 if (!strcmp(ttymodes[ind], key))
999 break;
1000 ind++;
1001 }
1002 dlg_listbox_select(td->modelist, dlg, ind);
1003 dlg_radiobutton_set(td->valradio, dlg,
1004 (*val == 'V'));
1005 dlg_editbox_set(td->valbox, dlg, val+1);
1006 }
1007 conf_del_str_str(conf, CONF_ttymodes, key);
1008 }
1009 i++;
1010 }
1011 dlg_refresh(td->listbox, dlg);
1012 }
1013 }
1014 }
1015
1016 struct environ_data {
1017 union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
1018 };
1019
1020 static void environ_handler(union control *ctrl, void *dlg,
1021 void *data, int event)
1022 {
1023 Conf *conf = (Conf *)data;
1024 struct environ_data *ed =
1025 (struct environ_data *)ctrl->generic.context.p;
1026
1027 if (event == EVENT_REFRESH) {
1028 if (ctrl == ed->listbox) {
1029 char *key, *val;
1030 dlg_update_start(ctrl, dlg);
1031 dlg_listbox_clear(ctrl, dlg);
1032 for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
1033 val != NULL;
1034 val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
1035 char *p = dupprintf("%s\t%s", key, val);
1036 dlg_listbox_add(ctrl, dlg, p);
1037 sfree(p);
1038 }
1039 dlg_update_done(ctrl, dlg);
1040 }
1041 } else if (event == EVENT_ACTION) {
1042 if (ctrl == ed->addbutton) {
1043 char *key, *val, *str;
1044 key = dlg_editbox_get(ed->varbox, dlg);
1045 if (!*key) {
1046 sfree(key);
1047 dlg_beep(dlg);
1048 return;
1049 }
1050 val = dlg_editbox_get(ed->valbox, dlg);
1051 if (!*val) {
1052 sfree(key);
1053 sfree(val);
1054 dlg_beep(dlg);
1055 return;
1056 }
1057 conf_set_str_str(conf, CONF_environmt, key, val);
1058 str = dupcat(key, "\t", val, NULL);
1059 dlg_editbox_set(ed->varbox, dlg, "");
1060 dlg_editbox_set(ed->valbox, dlg, "");
1061 sfree(str);
1062 sfree(key);
1063 sfree(val);
1064 dlg_refresh(ed->listbox, dlg);
1065 } else if (ctrl == ed->rembutton) {
1066 int i = dlg_listbox_index(ed->listbox, dlg);
1067 if (i < 0) {
1068 dlg_beep(dlg);
1069 } else {
1070 char *key, *val;
1071
1072 key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
1073 if (key) {
1074 /* Populate controls with the entry we're about to delete
1075 * for ease of editing */
1076 val = conf_get_str_str(conf, CONF_environmt, key);
1077 dlg_editbox_set(ed->varbox, dlg, key);
1078 dlg_editbox_set(ed->valbox, dlg, val);
1079 /* And delete it */
1080 conf_del_str_str(conf, CONF_environmt, key);
1081 }
1082 }
1083 dlg_refresh(ed->listbox, dlg);
1084 }
1085 }
1086 }
1087
1088 struct portfwd_data {
1089 union control *addbutton, *rembutton, *listbox;
1090 union control *sourcebox, *destbox, *direction;
1091 #ifndef NO_IPV6
1092 union control *addressfamily;
1093 #endif
1094 };
1095
1096 static void portfwd_handler(union control *ctrl, void *dlg,
1097 void *data, int event)
1098 {
1099 Conf *conf = (Conf *)data;
1100 struct portfwd_data *pfd =
1101 (struct portfwd_data *)ctrl->generic.context.p;
1102
1103 if (event == EVENT_REFRESH) {
1104 if (ctrl == pfd->listbox) {
1105 char *key, *val;
1106 dlg_update_start(ctrl, dlg);
1107 dlg_listbox_clear(ctrl, dlg);
1108 for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
1109 val != NULL;
1110 val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
1111 char *p;
1112 if (!strcmp(val, "D"))
1113 p = dupprintf("D%s\t", key+1);
1114 else
1115 p = dupprintf("%s\t%s", key, val);
1116 dlg_listbox_add(ctrl, dlg, p);
1117 sfree(p);
1118 }
1119 dlg_update_done(ctrl, dlg);
1120 } else if (ctrl == pfd->direction) {
1121 /*
1122 * Default is Local.
1123 */
1124 dlg_radiobutton_set(ctrl, dlg, 0);
1125 #ifndef NO_IPV6
1126 } else if (ctrl == pfd->addressfamily) {
1127 dlg_radiobutton_set(ctrl, dlg, 0);
1128 #endif
1129 }
1130 } else if (event == EVENT_ACTION) {
1131 if (ctrl == pfd->addbutton) {
1132 char *family, *type, *src, *key, *val;
1133 int i, whichbutton;
1134
1135 i = 0;
1136 #ifndef NO_IPV6
1137 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
1138 if (whichbutton == 1)
1139 family = "4";
1140 else if (whichbutton == 2)
1141 family = "6";
1142 else
1143 family = "";
1144 #endif
1145
1146 whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
1147 if (whichbutton == 0)
1148 type = "L";
1149 else if (whichbutton == 1)
1150 type = "R";
1151 else
1152 type = "D";
1153
1154 src = dlg_editbox_get(pfd->sourcebox, dlg);
1155 if (!*src) {
1156 dlg_error_msg(dlg, "You need to specify a source port number");
1157 sfree(src);
1158 return;
1159 }
1160 if (*type != 'D') {
1161 val = dlg_editbox_get(pfd->destbox, dlg);
1162 if (!*val || !strchr(val, ':')) {
1163 dlg_error_msg(dlg,
1164 "You need to specify a destination address\n"
1165 "in the form \"host.name:port\"");
1166 sfree(src);
1167 sfree(val);
1168 return;
1169 }
1170 } else {
1171 type = "L";
1172 val = dupstr("D"); /* special case */
1173 }
1174
1175 key = dupcat(family, type, src, NULL);
1176 sfree(src);
1177
1178 if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
1179 dlg_error_msg(dlg, "Specified forwarding already exists");
1180 } else {
1181 conf_set_str_str(conf, CONF_portfwd, key, val);
1182 }
1183
1184 sfree(key);
1185 sfree(val);
1186 dlg_refresh(pfd->listbox, dlg);
1187 } else if (ctrl == pfd->rembutton) {
1188 int i = dlg_listbox_index(pfd->listbox, dlg);
1189 if (i < 0) {
1190 dlg_beep(dlg);
1191 } else {
1192 char *key, *val, *p;
1193
1194 key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
1195 if (key) {
1196 static const char *const afs = "A46";
1197 static const char *const dirs = "LRD";
1198 char *afp;
1199 int dir;
1200 #ifndef NO_IPV6
1201 int idx;
1202 #endif
1203
1204 /* Populate controls with the entry we're about to delete
1205 * for ease of editing */
1206 p = key;
1207
1208 afp = strchr(afs, *p);
1209 #ifndef NO_IPV6
1210 idx = afp ? afp-afs : 0;
1211 #endif
1212 if (afp)
1213 p++;
1214 #ifndef NO_IPV6
1215 dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
1216 #endif
1217
1218 dir = *p;
1219
1220 val = conf_get_str_str(conf, CONF_portfwd, key);
1221 if (!strcmp(val, "D")) {
1222 dir = 'D';
1223 val = "";
1224 }
1225
1226 dlg_radiobutton_set(pfd->direction, dlg,
1227 strchr(dirs, dir) - dirs);
1228 p++;
1229
1230 dlg_editbox_set(pfd->sourcebox, dlg, p);
1231 dlg_editbox_set(pfd->destbox, dlg, val);
1232 /* And delete it */
1233 conf_del_str_str(conf, CONF_portfwd, key);
1234 }
1235 }
1236 dlg_refresh(pfd->listbox, dlg);
1237 }
1238 }
1239 }
1240
1241 void setup_config_box(struct controlbox *b, int midsession,
1242 int protocol, int protcfginfo)
1243 {
1244 struct controlset *s;
1245 struct sessionsaver_data *ssd;
1246 struct charclass_data *ccd;
1247 struct colour_data *cd;
1248 struct ttymodes_data *td;
1249 struct environ_data *ed;
1250 struct portfwd_data *pfd;
1251 union control *c;
1252 char *str;
1253
1254 ssd = (struct sessionsaver_data *)
1255 ctrl_alloc(b, sizeof(struct sessionsaver_data));
1256 memset(ssd, 0, sizeof(*ssd));
1257 ssd->midsession = midsession;
1258
1259 /*
1260 * The standard panel that appears at the bottom of all panels:
1261 * Open, Cancel, Apply etc.
1262 */
1263 s = ctrl_getset(b, "", "", "");
1264 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1265 ssd->okbutton = ctrl_pushbutton(s,
1266 (midsession ? "Apply" : "Open"),
1267 (char)(midsession ? 'a' : 'o'),
1268 HELPCTX(no_help),
1269 sessionsaver_handler, P(ssd));
1270 ssd->okbutton->button.isdefault = TRUE;
1271 ssd->okbutton->generic.column = 3;
1272 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1273 sessionsaver_handler, P(ssd));
1274 ssd->cancelbutton->button.iscancel = TRUE;
1275 ssd->cancelbutton->generic.column = 4;
1276 /* We carefully don't close the 5-column part, so that platform-
1277 * specific add-ons can put extra buttons alongside Open and Cancel. */
1278
1279 /*
1280 * The Session panel.
1281 */
1282 str = dupprintf("Basic options for your %s session", appname);
1283 ctrl_settitle(b, "Session", str);
1284 sfree(str);
1285
1286 if (!midsession) {
1287 struct hostport *hp = (struct hostport *)
1288 ctrl_alloc(b, sizeof(struct hostport));
1289
1290 s = ctrl_getset(b, "Session", "hostport",
1291 "Specify the destination you want to connect to");
1292 ctrl_columns(s, 2, 75, 25);
1293 c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1294 HELPCTX(session_hostname),
1295 config_host_handler, I(0), I(0));
1296 c->generic.column = 0;
1297 hp->host = c;
1298 c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1299 HELPCTX(session_hostname),
1300 config_port_handler, I(0), I(0));
1301 c->generic.column = 1;
1302 hp->port = c;
1303 ctrl_columns(s, 1, 100);
1304
1305 if (!backend_from_proto(PROT_SSH)) {
1306 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1307 HELPCTX(session_hostname),
1308 config_protocolbuttons_handler, P(hp),
1309 "Raw", 'w', I(PROT_RAW),
1310 "Telnet", 't', I(PROT_TELNET),
1311 "Rlogin", 'i', I(PROT_RLOGIN),
1312 NULL);
1313 } else {
1314 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1315 HELPCTX(session_hostname),
1316 config_protocolbuttons_handler, P(hp),
1317 "Raw", 'w', I(PROT_RAW),
1318 "Telnet", 't', I(PROT_TELNET),
1319 "Rlogin", 'i', I(PROT_RLOGIN),
1320 "SSH", 's', I(PROT_SSH),
1321 NULL);
1322 }
1323 }
1324
1325 /*
1326 * The Load/Save panel is available even in mid-session.
1327 */
1328 s = ctrl_getset(b, "Session", "savedsessions",
1329 midsession ? "Save the current session settings" :
1330 "Load, save or delete a stored session");
1331 ctrl_columns(s, 2, 75, 25);
1332 get_sesslist(&ssd->sesslist, TRUE);
1333 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1334 HELPCTX(session_saved),
1335 sessionsaver_handler, P(ssd), P(NULL));
1336 ssd->editbox->generic.column = 0;
1337 /* Reset columns so that the buttons are alongside the list, rather
1338 * than alongside that edit box. */
1339 ctrl_columns(s, 1, 100);
1340 ctrl_columns(s, 2, 75, 25);
1341 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1342 HELPCTX(session_saved),
1343 sessionsaver_handler, P(ssd));
1344 ssd->listbox->generic.column = 0;
1345 ssd->listbox->listbox.height = 7;
1346 if (!midsession) {
1347 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1348 HELPCTX(session_saved),
1349 sessionsaver_handler, P(ssd));
1350 ssd->loadbutton->generic.column = 1;
1351 } else {
1352 /* We can't offer the Load button mid-session, as it would allow the
1353 * user to load and subsequently save settings they can't see. (And
1354 * also change otherwise immutable settings underfoot; that probably
1355 * shouldn't be a problem, but.) */
1356 ssd->loadbutton = NULL;
1357 }
1358 /* "Save" button is permitted mid-session. */
1359 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1360 HELPCTX(session_saved),
1361 sessionsaver_handler, P(ssd));
1362 ssd->savebutton->generic.column = 1;
1363 if (!midsession) {
1364 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1365 HELPCTX(session_saved),
1366 sessionsaver_handler, P(ssd));
1367 ssd->delbutton->generic.column = 1;
1368 } else {
1369 /* Disable the Delete button mid-session too, for UI consistency. */
1370 ssd->delbutton = NULL;
1371 }
1372 ctrl_columns(s, 1, 100);
1373
1374 s = ctrl_getset(b, "Session", "otheropts", NULL);
1375 c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
1376 HELPCTX(session_coe),
1377 conf_radiobutton_handler,
1378 I(CONF_close_on_exit),
1379 "Always", I(FORCE_ON),
1380 "Never", I(FORCE_OFF),
1381 "Only on clean exit", I(AUTO), NULL);
1382
1383 /*
1384 * The Session/Logging panel.
1385 */
1386 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1387
1388 s = ctrl_getset(b, "Session/Logging", "main", NULL);
1389 /*
1390 * The logging buttons change depending on whether SSH packet
1391 * logging can sensibly be available.
1392 */
1393 {
1394 char *sshlogname, *sshrawlogname;
1395 if ((midsession && protocol == PROT_SSH) ||
1396 (!midsession && backend_from_proto(PROT_SSH))) {
1397 sshlogname = "SSH packets";
1398 sshrawlogname = "SSH packets and raw data";
1399 } else {
1400 sshlogname = NULL; /* this will disable both buttons */
1401 sshrawlogname = NULL; /* this will just placate optimisers */
1402 }
1403 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1404 HELPCTX(logging_main),
1405 loggingbuttons_handler,
1406 I(CONF_logtype),
1407 "None", 't', I(LGTYP_NONE),
1408 "Printable output", 'p', I(LGTYP_ASCII),
1409 "All session output", 'l', I(LGTYP_DEBUG),
1410 sshlogname, 's', I(LGTYP_PACKETS),
1411 sshrawlogname, 'r', I(LGTYP_SSHRAW),
1412 NULL);
1413 }
1414 ctrl_filesel(s, "Log file name:", 'f',
1415 NULL, TRUE, "Select session log file name",
1416 HELPCTX(logging_filename),
1417 conf_filesel_handler, I(CONF_logfilename));
1418 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1419 " &T for time, and &H for host name)",
1420 HELPCTX(logging_filename));
1421 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1422 HELPCTX(logging_exists),
1423 conf_radiobutton_handler, I(CONF_logxfovr),
1424 "Always overwrite it", I(LGXF_OVR),
1425 "Always append to the end of it", I(LGXF_APN),
1426 "Ask the user every time", I(LGXF_ASK), NULL);
1427 ctrl_checkbox(s, "Flush log file frequently", 'u',
1428 HELPCTX(logging_flush),
1429 conf_checkbox_handler, I(CONF_logflush));
1430
1431 if ((midsession && protocol == PROT_SSH) ||
1432 (!midsession && backend_from_proto(PROT_SSH))) {
1433 s = ctrl_getset(b, "Session/Logging", "ssh",
1434 "Options specific to SSH packet logging");
1435 ctrl_checkbox(s, "Omit known password fields", 'k',
1436 HELPCTX(logging_ssh_omit_password),
1437 conf_checkbox_handler, I(CONF_logomitpass));
1438 ctrl_checkbox(s, "Omit session data", 'd',
1439 HELPCTX(logging_ssh_omit_data),
1440 conf_checkbox_handler, I(CONF_logomitdata));
1441 }
1442
1443 /*
1444 * The Terminal panel.
1445 */
1446 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1447
1448 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1449 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1450 HELPCTX(terminal_autowrap),
1451 conf_checkbox_handler, I(CONF_wrap_mode));
1452 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1453 HELPCTX(terminal_decom),
1454 conf_checkbox_handler, I(CONF_dec_om));
1455 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1456 HELPCTX(terminal_lfhascr),
1457 conf_checkbox_handler, I(CONF_lfhascr));
1458 ctrl_checkbox(s, "Implicit LF in every CR", 'f',
1459 HELPCTX(terminal_crhaslf),
1460 conf_checkbox_handler, I(CONF_crhaslf));
1461 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1462 HELPCTX(terminal_bce),
1463 conf_checkbox_handler, I(CONF_bce));
1464 ctrl_checkbox(s, "Enable blinking text", 'n',
1465 HELPCTX(terminal_blink),
1466 conf_checkbox_handler, I(CONF_blinktext));
1467 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1468 HELPCTX(terminal_answerback),
1469 conf_editbox_handler, I(CONF_answerback), I(1));
1470
1471 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1472 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1473 HELPCTX(terminal_localecho),
1474 conf_radiobutton_handler,I(CONF_localecho),
1475 "Auto", I(AUTO),
1476 "Force on", I(FORCE_ON),
1477 "Force off", I(FORCE_OFF), NULL);
1478 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1479 HELPCTX(terminal_localedit),
1480 conf_radiobutton_handler,I(CONF_localedit),
1481 "Auto", I(AUTO),
1482 "Force on", I(FORCE_ON),
1483 "Force off", I(FORCE_OFF), NULL);
1484
1485 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1486 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1487 HELPCTX(terminal_printing),
1488 printerbox_handler, P(NULL), P(NULL));
1489
1490 /*
1491 * The Terminal/Keyboard panel.
1492 */
1493 ctrl_settitle(b, "Terminal/Keyboard",
1494 "Options controlling the effects of keys");
1495
1496 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1497 "Change the sequences sent by:");
1498 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1499 HELPCTX(keyboard_backspace),
1500 conf_radiobutton_handler,
1501 I(CONF_bksp_is_delete),
1502 "Control-H", I(0), "Control-? (127)", I(1), NULL);
1503 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1504 HELPCTX(keyboard_homeend),
1505 conf_radiobutton_handler,
1506 I(CONF_rxvt_homeend),
1507 "Standard", I(0), "rxvt", I(1), NULL);
1508 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1509 HELPCTX(keyboard_funkeys),
1510 conf_radiobutton_handler,
1511 I(CONF_funky_type),
1512 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1513 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1514
1515 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1516 "Application keypad settings:");
1517 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1518 HELPCTX(keyboard_appcursor),
1519 conf_radiobutton_handler,
1520 I(CONF_app_cursor),
1521 "Normal", I(0), "Application", I(1), NULL);
1522 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1523 HELPCTX(keyboard_appkeypad),
1524 numeric_keypad_handler, P(NULL),
1525 "Normal", I(0), "Application", I(1), "NetHack", I(2),
1526 NULL);
1527
1528 /*
1529 * The Terminal/Bell panel.
1530 */
1531 ctrl_settitle(b, "Terminal/Bell",
1532 "Options controlling the terminal bell");
1533
1534 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1535 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1536 HELPCTX(bell_style),
1537 conf_radiobutton_handler, I(CONF_beep),
1538 "None (bell disabled)", I(BELL_DISABLED),
1539 "Make default system alert sound", I(BELL_DEFAULT),
1540 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1541
1542 s = ctrl_getset(b, "Terminal/Bell", "overload",
1543 "Control the bell overload behaviour");
1544 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1545 HELPCTX(bell_overload),
1546 conf_checkbox_handler, I(CONF_bellovl));
1547 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1548 HELPCTX(bell_overload),
1549 conf_editbox_handler, I(CONF_bellovl_n), I(-1));
1550 ctrl_editbox(s, "... in this many seconds", 't', 20,
1551 HELPCTX(bell_overload),
1552 conf_editbox_handler, I(CONF_bellovl_t),
1553 I(-TICKSPERSEC));
1554 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1555 HELPCTX(bell_overload));
1556 ctrl_editbox(s, "Seconds of silence required", 's', 20,
1557 HELPCTX(bell_overload),
1558 conf_editbox_handler, I(CONF_bellovl_s),
1559 I(-TICKSPERSEC));
1560
1561 /*
1562 * The Terminal/Features panel.
1563 */
1564 ctrl_settitle(b, "Terminal/Features",
1565 "Enabling and disabling advanced terminal features");
1566
1567 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1568 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1569 HELPCTX(features_application),
1570 conf_checkbox_handler, I(CONF_no_applic_c));
1571 ctrl_checkbox(s, "Disable application keypad mode", 'k',
1572 HELPCTX(features_application),
1573 conf_checkbox_handler, I(CONF_no_applic_k));
1574 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1575 HELPCTX(features_mouse),
1576 conf_checkbox_handler, I(CONF_no_mouse_rep));
1577 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1578 HELPCTX(features_resize),
1579 conf_checkbox_handler,
1580 I(CONF_no_remote_resize));
1581 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1582 HELPCTX(features_altscreen),
1583 conf_checkbox_handler, I(CONF_no_alt_screen));
1584 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1585 HELPCTX(features_retitle),
1586 conf_checkbox_handler,
1587 I(CONF_no_remote_wintitle));
1588 ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1589 HELPCTX(features_qtitle),
1590 conf_radiobutton_handler,
1591 I(CONF_remote_qtitle_action),
1592 "None", I(TITLE_NONE),
1593 "Empty string", I(TITLE_EMPTY),
1594 "Window title", I(TITLE_REAL), NULL);
1595 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1596 HELPCTX(features_dbackspace),
1597 conf_checkbox_handler, I(CONF_no_dbackspace));
1598 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1599 'r', HELPCTX(features_charset), conf_checkbox_handler,
1600 I(CONF_no_remote_charset));
1601 ctrl_checkbox(s, "Disable Arabic text shaping",
1602 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
1603 I(CONF_arabicshaping));
1604 ctrl_checkbox(s, "Disable bidirectional text display",
1605 'd', HELPCTX(features_bidi), conf_checkbox_handler,
1606 I(CONF_bidi));
1607
1608 /*
1609 * The Window panel.
1610 */
1611 str = dupprintf("Options controlling %s's window", appname);
1612 ctrl_settitle(b, "Window", str);
1613 sfree(str);
1614
1615 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1616 ctrl_columns(s, 2, 50, 50);
1617 c = ctrl_editbox(s, "Columns", 'm', 100,
1618 HELPCTX(window_size),
1619 conf_editbox_handler, I(CONF_width), I(-1));
1620 c->generic.column = 0;
1621 c = ctrl_editbox(s, "Rows", 'r', 100,
1622 HELPCTX(window_size),
1623 conf_editbox_handler, I(CONF_height),I(-1));
1624 c->generic.column = 1;
1625 ctrl_columns(s, 1, 100);
1626
1627 s = ctrl_getset(b, "Window", "scrollback",
1628 "Control the scrollback in the window");
1629 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1630 HELPCTX(window_scrollback),
1631 conf_editbox_handler, I(CONF_savelines), I(-1));
1632 ctrl_checkbox(s, "Display scrollbar", 'd',
1633 HELPCTX(window_scrollback),
1634 conf_checkbox_handler, I(CONF_scrollbar));
1635 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1636 HELPCTX(window_scrollback),
1637 conf_checkbox_handler, I(CONF_scroll_on_key));
1638 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1639 HELPCTX(window_scrollback),
1640 conf_checkbox_handler, I(CONF_scroll_on_disp));
1641 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1642 HELPCTX(window_erased),
1643 conf_checkbox_handler,
1644 I(CONF_erase_to_scrollback));
1645
1646 /*
1647 * The Window/Appearance panel.
1648 */
1649 str = dupprintf("Configure the appearance of %s's window", appname);
1650 ctrl_settitle(b, "Window/Appearance", str);
1651 sfree(str);
1652
1653 s = ctrl_getset(b, "Window/Appearance", "cursor",
1654 "Adjust the use of the cursor");
1655 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1656 HELPCTX(appearance_cursor),
1657 conf_radiobutton_handler,
1658 I(CONF_cursor_type),
1659 "Block", 'l', I(0),
1660 "Underline", 'u', I(1),
1661 "Vertical line", 'v', I(2), NULL);
1662 ctrl_checkbox(s, "Cursor blinks", 'b',
1663 HELPCTX(appearance_cursor),
1664 conf_checkbox_handler, I(CONF_blink_cur));
1665
1666 s = ctrl_getset(b, "Window/Appearance", "font",
1667 "Font settings");
1668 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1669 HELPCTX(appearance_font),
1670 conf_fontsel_handler, I(CONF_font));
1671
1672 s = ctrl_getset(b, "Window/Appearance", "mouse",
1673 "Adjust the use of the mouse pointer");
1674 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1675 HELPCTX(appearance_hidemouse),
1676 conf_checkbox_handler, I(CONF_hide_mouseptr));
1677
1678 s = ctrl_getset(b, "Window/Appearance", "border",
1679 "Adjust the window border");
1680 ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1681 HELPCTX(appearance_border),
1682 conf_editbox_handler,
1683 I(CONF_window_border), I(-1));
1684
1685 /*
1686 * The Window/Behaviour panel.
1687 */
1688 str = dupprintf("Configure the behaviour of %s's window", appname);
1689 ctrl_settitle(b, "Window/Behaviour", str);
1690 sfree(str);
1691
1692 s = ctrl_getset(b, "Window/Behaviour", "title",
1693 "Adjust the behaviour of the window title");
1694 ctrl_editbox(s, "Window title:", 't', 100,
1695 HELPCTX(appearance_title),
1696 conf_editbox_handler, I(CONF_wintitle), I(1));
1697 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1698 HELPCTX(appearance_title),
1699 conf_checkbox_handler,
1700 I(CHECKBOX_INVERT | CONF_win_name_always));
1701
1702 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1703 ctrl_checkbox(s, "Warn before closing window", 'w',
1704 HELPCTX(behaviour_closewarn),
1705 conf_checkbox_handler, I(CONF_warn_on_close));
1706
1707 /*
1708 * The Window/Translation panel.
1709 */
1710 ctrl_settitle(b, "Window/Translation",
1711 "Options controlling character set translation");
1712
1713 s = ctrl_getset(b, "Window/Translation", "trans",
1714 "Character set translation");
1715 ctrl_combobox(s, "Remote character set:",
1716 'r', 100, HELPCTX(translation_codepage),
1717 codepage_handler, P(NULL), P(NULL));
1718
1719 s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1720 ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1721 HELPCTX(translation_cjk_ambig_wide),
1722 conf_checkbox_handler, I(CONF_cjk_ambig_wide));
1723
1724 str = dupprintf("Adjust how %s handles line drawing characters", appname);
1725 s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1726 sfree(str);
1727 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1728 HELPCTX(translation_linedraw),
1729 conf_radiobutton_handler,
1730 I(CONF_vtmode),
1731 "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1732 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1733 NULL);
1734 ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1735 HELPCTX(selection_linedraw),
1736 conf_checkbox_handler, I(CONF_rawcnp));
1737
1738 /*
1739 * The Window/Selection panel.
1740 */
1741 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1742
1743 s = ctrl_getset(b, "Window/Selection", "mouse",
1744 "Control use of mouse");
1745 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1746 HELPCTX(selection_shiftdrag),
1747 conf_checkbox_handler, I(CONF_mouse_override));
1748 ctrl_radiobuttons(s,
1749 "Default selection mode (Alt+drag does the other one):",
1750 NO_SHORTCUT, 2,
1751 HELPCTX(selection_rect),
1752 conf_radiobutton_handler,
1753 I(CONF_rect_select),
1754 "Normal", 'n', I(0),
1755 "Rectangular block", 'r', I(1), NULL);
1756
1757 s = ctrl_getset(b, "Window/Selection", "charclass",
1758 "Control the select-one-word-at-a-time mode");
1759 ccd = (struct charclass_data *)
1760 ctrl_alloc(b, sizeof(struct charclass_data));
1761 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1762 HELPCTX(selection_charclasses),
1763 charclass_handler, P(ccd));
1764 ccd->listbox->listbox.multisel = 1;
1765 ccd->listbox->listbox.ncols = 4;
1766 ccd->listbox->listbox.percentages = snewn(4, int);
1767 ccd->listbox->listbox.percentages[0] = 15;
1768 ccd->listbox->listbox.percentages[1] = 25;
1769 ccd->listbox->listbox.percentages[2] = 20;
1770 ccd->listbox->listbox.percentages[3] = 40;
1771 ctrl_columns(s, 2, 67, 33);
1772 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1773 HELPCTX(selection_charclasses),
1774 charclass_handler, P(ccd), P(NULL));
1775 ccd->editbox->generic.column = 0;
1776 ccd->button = ctrl_pushbutton(s, "Set", 's',
1777 HELPCTX(selection_charclasses),
1778 charclass_handler, P(ccd));
1779 ccd->button->generic.column = 1;
1780 ctrl_columns(s, 1, 100);
1781
1782 /*
1783 * The Window/Colours panel.
1784 */
1785 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1786
1787 s = ctrl_getset(b, "Window/Colours", "general",
1788 "General options for colour usage");
1789 ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1790 HELPCTX(colours_ansi),
1791 conf_checkbox_handler, I(CONF_ansi_colour));
1792 ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1793 HELPCTX(colours_xterm256), conf_checkbox_handler,
1794 I(CONF_xterm_256_colour));
1795 ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1796 HELPCTX(colours_bold),
1797 conf_checkbox_handler, I(CONF_bold_colour));
1798
1799 str = dupprintf("Adjust the precise colours %s displays", appname);
1800 s = ctrl_getset(b, "Window/Colours", "adjust", str);
1801 sfree(str);
1802 ctrl_text(s, "Select a colour from the list, and then click the"
1803 " Modify button to change its appearance.",
1804 HELPCTX(colours_config));
1805 ctrl_columns(s, 2, 67, 33);
1806 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1807 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1808 HELPCTX(colours_config), colour_handler, P(cd));
1809 cd->listbox->generic.column = 0;
1810 cd->listbox->listbox.height = 7;
1811 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1812 c->generic.column = 1;
1813 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1814 colour_handler, P(cd), P(NULL));
1815 cd->redit->generic.column = 1;
1816 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1817 colour_handler, P(cd), P(NULL));
1818 cd->gedit->generic.column = 1;
1819 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1820 colour_handler, P(cd), P(NULL));
1821 cd->bedit->generic.column = 1;
1822 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1823 colour_handler, P(cd));
1824 cd->button->generic.column = 1;
1825 ctrl_columns(s, 1, 100);
1826
1827 /*
1828 * The Connection panel. This doesn't show up if we're in a
1829 * non-network utility such as pterm. We tell this by being
1830 * passed a protocol < 0.
1831 */
1832 if (protocol >= 0) {
1833 ctrl_settitle(b, "Connection", "Options controlling the connection");
1834
1835 s = ctrl_getset(b, "Connection", "keepalive",
1836 "Sending of null packets to keep session active");
1837 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1838 HELPCTX(connection_keepalive),
1839 conf_editbox_handler, I(CONF_ping_interval),
1840 I(-1));
1841
1842 if (!midsession) {
1843 s = ctrl_getset(b, "Connection", "tcp",
1844 "Low-level TCP connection options");
1845 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1846 'n', HELPCTX(connection_nodelay),
1847 conf_checkbox_handler,
1848 I(CONF_tcp_nodelay));
1849 ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1850 'p', HELPCTX(connection_tcpkeepalive),
1851 conf_checkbox_handler,
1852 I(CONF_tcp_keepalives));
1853 #ifndef NO_IPV6
1854 s = ctrl_getset(b, "Connection", "ipversion",
1855 "Internet protocol version");
1856 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1857 HELPCTX(connection_ipversion),
1858 conf_radiobutton_handler,
1859 I(CONF_addressfamily),
1860 "Auto", 'u', I(ADDRTYPE_UNSPEC),
1861 "IPv4", '4', I(ADDRTYPE_IPV4),
1862 "IPv6", '6', I(ADDRTYPE_IPV6),
1863 NULL);
1864 #endif
1865
1866 {
1867 char *label = backend_from_proto(PROT_SSH) ?
1868 "Logical name of remote host (e.g. for SSH key lookup):" :
1869 "Logical name of remote host:";
1870 s = ctrl_getset(b, "Connection", "identity",
1871 "Logical name of remote host");
1872 ctrl_editbox(s, label, 'm', 100,
1873 HELPCTX(connection_loghost),
1874 conf_editbox_handler, I(CONF_loghost), I(1));
1875 }
1876 }
1877
1878 /*
1879 * A sub-panel Connection/Data, containing options that
1880 * decide on data to send to the server.
1881 */
1882 if (!midsession) {
1883 ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1884
1885 s = ctrl_getset(b, "Connection/Data", "login",
1886 "Login details");
1887 ctrl_editbox(s, "Auto-login username", 'u', 50,
1888 HELPCTX(connection_username),
1889 conf_editbox_handler, I(CONF_username), I(1));
1890 {
1891 /* We assume the local username is sufficiently stable
1892 * to include on the dialog box. */
1893 char *user = get_username();
1894 char *userlabel = dupprintf("Use system username (%s)",
1895 user ? user : "");
1896 sfree(user);
1897 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
1898 HELPCTX(connection_username_from_env),
1899 conf_radiobutton_handler,
1900 I(CONF_username_from_env),
1901 "Prompt", I(FALSE),
1902 userlabel, I(TRUE),
1903 NULL);
1904 sfree(userlabel);
1905 }
1906
1907 s = ctrl_getset(b, "Connection/Data", "term",
1908 "Terminal details");
1909 ctrl_editbox(s, "Terminal-type string", 't', 50,
1910 HELPCTX(connection_termtype),
1911 conf_editbox_handler, I(CONF_termtype), I(1));
1912 ctrl_editbox(s, "Terminal speeds", 's', 50,
1913 HELPCTX(connection_termspeed),
1914 conf_editbox_handler, I(CONF_termspeed), I(1));
1915
1916 s = ctrl_getset(b, "Connection/Data", "env",
1917 "Environment variables");
1918 ctrl_columns(s, 2, 80, 20);
1919 ed = (struct environ_data *)
1920 ctrl_alloc(b, sizeof(struct environ_data));
1921 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1922 HELPCTX(telnet_environ),
1923 environ_handler, P(ed), P(NULL));
1924 ed->varbox->generic.column = 0;
1925 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1926 HELPCTX(telnet_environ),
1927 environ_handler, P(ed), P(NULL));
1928 ed->valbox->generic.column = 0;
1929 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1930 HELPCTX(telnet_environ),
1931 environ_handler, P(ed));
1932 ed->addbutton->generic.column = 1;
1933 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1934 HELPCTX(telnet_environ),
1935 environ_handler, P(ed));
1936 ed->rembutton->generic.column = 1;
1937 ctrl_columns(s, 1, 100);
1938 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1939 HELPCTX(telnet_environ),
1940 environ_handler, P(ed));
1941 ed->listbox->listbox.height = 3;
1942 ed->listbox->listbox.ncols = 2;
1943 ed->listbox->listbox.percentages = snewn(2, int);
1944 ed->listbox->listbox.percentages[0] = 30;
1945 ed->listbox->listbox.percentages[1] = 70;
1946 }
1947
1948 }
1949
1950 if (!midsession) {
1951 /*
1952 * The Connection/Proxy panel.
1953 */
1954 ctrl_settitle(b, "Connection/Proxy",
1955 "Options controlling proxy usage");
1956
1957 s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1958 ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1959 HELPCTX(proxy_type),
1960 conf_radiobutton_handler,
1961 I(CONF_proxy_type),
1962 "None", I(PROXY_NONE),
1963 "SOCKS 4", I(PROXY_SOCKS4),
1964 "SOCKS 5", I(PROXY_SOCKS5),
1965 "HTTP", I(PROXY_HTTP),
1966 "Telnet", I(PROXY_TELNET),
1967 NULL);
1968 ctrl_columns(s, 2, 80, 20);
1969 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1970 HELPCTX(proxy_main),
1971 conf_editbox_handler,
1972 I(CONF_proxy_host), I(1));
1973 c->generic.column = 0;
1974 c = ctrl_editbox(s, "Port", 'p', 100,
1975 HELPCTX(proxy_main),
1976 conf_editbox_handler,
1977 I(CONF_proxy_port),
1978 I(-1));
1979 c->generic.column = 1;
1980 ctrl_columns(s, 1, 100);
1981 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1982 HELPCTX(proxy_exclude),
1983 conf_editbox_handler,
1984 I(CONF_proxy_exclude_list), I(1));
1985 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1986 HELPCTX(proxy_exclude),
1987 conf_checkbox_handler,
1988 I(CONF_even_proxy_localhost));
1989 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1990 HELPCTX(proxy_dns),
1991 conf_radiobutton_handler,
1992 I(CONF_proxy_dns),
1993 "No", I(FORCE_OFF),
1994 "Auto", I(AUTO),
1995 "Yes", I(FORCE_ON), NULL);
1996 ctrl_editbox(s, "Username", 'u', 60,
1997 HELPCTX(proxy_auth),
1998 conf_editbox_handler,
1999 I(CONF_proxy_username), I(1));
2000 c = ctrl_editbox(s, "Password", 'w', 60,
2001 HELPCTX(proxy_auth),
2002 conf_editbox_handler,
2003 I(CONF_proxy_password), I(1));
2004 c->editbox.password = 1;
2005 ctrl_editbox(s, "Telnet command", 'm', 100,
2006 HELPCTX(proxy_command),
2007 conf_editbox_handler,
2008 I(CONF_proxy_telnet_command), I(1));
2009 }
2010
2011 /*
2012 * The Telnet panel exists in the base config box, and in a
2013 * mid-session reconfig box _if_ we're using Telnet.
2014 */
2015 if (!midsession || protocol == PROT_TELNET) {
2016 /*
2017 * The Connection/Telnet panel.
2018 */
2019 ctrl_settitle(b, "Connection/Telnet",
2020 "Options controlling Telnet connections");
2021
2022 s = ctrl_getset(b, "Connection/Telnet", "protocol",
2023 "Telnet protocol adjustments");
2024
2025 if (!midsession) {
2026 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
2027 NO_SHORTCUT, 2,
2028 HELPCTX(telnet_oldenviron),
2029 conf_radiobutton_handler,
2030 I(CONF_rfc_environ),
2031 "BSD (commonplace)", 'b', I(0),
2032 "RFC 1408 (unusual)", 'f', I(1), NULL);
2033 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
2034 HELPCTX(telnet_passive),
2035 conf_radiobutton_handler,
2036 I(CONF_passive_telnet),
2037 "Passive", I(1), "Active", I(0), NULL);
2038 }
2039 ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
2040 HELPCTX(telnet_specialkeys),
2041 conf_checkbox_handler,
2042 I(CONF_telnet_keyboard));
2043 ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
2044 'm', HELPCTX(telnet_newline),
2045 conf_checkbox_handler,
2046 I(CONF_telnet_newline));
2047 }
2048
2049 if (!midsession) {
2050
2051 /*
2052 * The Connection/Rlogin panel.
2053 */
2054 ctrl_settitle(b, "Connection/Rlogin",
2055 "Options controlling Rlogin connections");
2056
2057 s = ctrl_getset(b, "Connection/Rlogin", "data",
2058 "Data to send to the server");
2059 ctrl_editbox(s, "Local username:", 'l', 50,
2060 HELPCTX(rlogin_localuser),
2061 conf_editbox_handler, I(CONF_localusername), I(1));
2062
2063 }
2064
2065 /*
2066 * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
2067 * when we're not doing SSH.
2068 */
2069
2070 if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
2071
2072 /*
2073 * The Connection/SSH panel.
2074 */
2075 ctrl_settitle(b, "Connection/SSH",
2076 "Options controlling SSH connections");
2077
2078 if (midsession && protcfginfo == 1) {
2079 s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
2080 ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
2081 "session; it is only here so that sub-panels of it can "
2082 "exist without looking strange.", HELPCTX(no_help));
2083 }
2084
2085 if (!midsession) {
2086
2087 s = ctrl_getset(b, "Connection/SSH", "data",
2088 "Data to send to the server");
2089 ctrl_editbox(s, "Remote command:", 'r', 100,
2090 HELPCTX(ssh_command),
2091 conf_editbox_handler, I(CONF_remote_cmd), I(1));
2092
2093 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2094 ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
2095 HELPCTX(ssh_noshell),
2096 conf_checkbox_handler,
2097 I(CONF_ssh_no_shell));
2098 }
2099
2100 if (!midsession || protcfginfo != 1) {
2101 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2102
2103 ctrl_checkbox(s, "Enable compression", 'e',
2104 HELPCTX(ssh_compress),
2105 conf_checkbox_handler,
2106 I(CONF_compression));
2107 }
2108
2109 if (!midsession) {
2110 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
2111
2112 ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
2113 HELPCTX(ssh_protocol),
2114 conf_radiobutton_handler,
2115 I(CONF_sshprot),
2116 "1 only", 'l', I(0),
2117 "1", '1', I(1),
2118 "2", '2', I(2),
2119 "2 only", 'y', I(3), NULL);
2120 }
2121
2122 if (!midsession || protcfginfo != 1) {
2123 s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
2124 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
2125 HELPCTX(ssh_ciphers),
2126 cipherlist_handler, P(NULL));
2127 c->listbox.height = 6;
2128
2129 ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
2130 HELPCTX(ssh_ciphers),
2131 conf_checkbox_handler,
2132 I(CONF_ssh2_des_cbc));
2133 }
2134
2135 /*
2136 * The Connection/SSH/Kex panel. (Owing to repeat key
2137 * exchange, this is all meaningful in mid-session _if_
2138 * we're using SSH-2 or haven't decided yet.)
2139 */
2140 if (protcfginfo != 1) {
2141 ctrl_settitle(b, "Connection/SSH/Kex",
2142 "Options controlling SSH key exchange");
2143
2144 s = ctrl_getset(b, "Connection/SSH/Kex", "main",
2145 "Key exchange algorithm options");
2146 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
2147 HELPCTX(ssh_kexlist),
2148 kexlist_handler, P(NULL));
2149 c->listbox.height = 5;
2150
2151 s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
2152 "Options controlling key re-exchange");
2153
2154 ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
2155 HELPCTX(ssh_kex_repeat),
2156 conf_editbox_handler,
2157 I(CONF_ssh_rekey_time),
2158 I(-1));
2159 ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
2160 HELPCTX(ssh_kex_repeat),
2161 conf_editbox_handler,
2162 I(CONF_ssh_rekey_data),
2163 I(16));
2164 ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
2165 HELPCTX(ssh_kex_repeat));
2166 }
2167
2168 if (!midsession) {
2169
2170 /*
2171 * The Connection/SSH/Auth panel.
2172 */
2173 ctrl_settitle(b, "Connection/SSH/Auth",
2174 "Options controlling SSH authentication");
2175
2176 s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
2177 ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
2178 HELPCTX(ssh_auth_bypass),
2179 conf_checkbox_handler,
2180 I(CONF_ssh_no_userauth));
2181 ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
2182 'd', HELPCTX(ssh_auth_banner),
2183 conf_checkbox_handler,
2184 I(CONF_ssh_show_banner));
2185
2186 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
2187 "Authentication methods");
2188 ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
2189 HELPCTX(ssh_auth_pageant),
2190 conf_checkbox_handler,
2191 I(CONF_tryagent));
2192 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
2193 HELPCTX(ssh_auth_tis),
2194 conf_checkbox_handler,
2195 I(CONF_try_tis_auth));
2196 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
2197 'i', HELPCTX(ssh_auth_ki),
2198 conf_checkbox_handler,
2199 I(CONF_try_ki_auth));
2200
2201 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
2202 "Authentication parameters");
2203 ctrl_checkbox(s, "Allow agent forwarding", 'f',
2204 HELPCTX(ssh_auth_agentfwd),
2205 conf_checkbox_handler, I(CONF_agentfwd));
2206 ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
2207 HELPCTX(ssh_auth_changeuser),
2208 conf_checkbox_handler,
2209 I(CONF_change_username));
2210 ctrl_filesel(s, "Private key file for authentication:", 'k',
2211 FILTER_KEY_FILES, FALSE, "Select private key file",
2212 HELPCTX(ssh_auth_privkey),
2213 conf_filesel_handler, I(CONF_keyfile));
2214
2215 #ifndef NO_GSSAPI
2216 /*
2217 * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
2218 * the main Auth panel.
2219 */
2220 ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
2221 "Options controlling GSSAPI authentication");
2222 s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
2223
2224 ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
2225 't', HELPCTX(ssh_gssapi),
2226 conf_checkbox_handler,
2227 I(CONF_try_gssapi_auth));
2228
2229 ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
2230 HELPCTX(ssh_gssapi_delegation),
2231 conf_checkbox_handler,
2232 I(CONF_gssapifwd));
2233
2234 /*
2235 * GSSAPI library selection.
2236 */
2237 if (ngsslibs > 1) {
2238 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
2239 'p', HELPCTX(ssh_gssapi_libraries),
2240 gsslist_handler, P(NULL));
2241 c->listbox.height = ngsslibs;
2242
2243 /*
2244 * I currently assume that if more than one GSS
2245 * library option is available, then one of them is
2246 * 'user-supplied' and so we should present the
2247 * following file selector. This is at least half-
2248 * reasonable, because if we're using statically
2249 * linked GSSAPI then there will only be one option
2250 * and no way to load from a user-supplied library,
2251 * whereas if we're using dynamic libraries then
2252 * there will almost certainly be some default
2253 * option in addition to a user-supplied path. If
2254 * anyone ever ports PuTTY to a system on which
2255 * dynamic-library GSSAPI is available but there is
2256 * absolutely no consensus on where to keep the
2257 * libraries, there'll need to be a flag alongside
2258 * ngsslibs to control whether the file selector is
2259 * displayed.
2260 */
2261
2262 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
2263 FILTER_DYNLIB_FILES, FALSE, "Select library file",
2264 HELPCTX(ssh_gssapi_libraries),
2265 conf_filesel_handler,
2266 I(CONF_ssh_gss_custom));
2267 }
2268 #endif
2269 }
2270
2271 if (!midsession) {
2272 /*
2273 * The Connection/SSH/TTY panel.
2274 */
2275 ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
2276
2277 s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
2278 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
2279 HELPCTX(ssh_nopty),
2280 conf_checkbox_handler,
2281 I(CONF_nopty));
2282
2283 s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
2284 "Terminal modes");
2285 td = (struct ttymodes_data *)
2286 ctrl_alloc(b, sizeof(struct ttymodes_data));
2287 ctrl_columns(s, 2, 75, 25);
2288 c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
2289 c->generic.column = 0;
2290 td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2291 HELPCTX(ssh_ttymodes),
2292 ttymodes_handler, P(td));
2293 td->rembutton->generic.column = 1;
2294 td->rembutton->generic.tabdelay = 1;
2295 ctrl_columns(s, 1, 100);
2296 td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2297 HELPCTX(ssh_ttymodes),
2298 ttymodes_handler, P(td));
2299 td->listbox->listbox.multisel = 1;
2300 td->listbox->listbox.height = 4;
2301 td->listbox->listbox.ncols = 2;
2302 td->listbox->listbox.percentages = snewn(2, int);
2303 td->listbox->listbox.percentages[0] = 40;
2304 td->listbox->listbox.percentages[1] = 60;
2305 ctrl_tabdelay(s, td->rembutton);
2306 ctrl_columns(s, 2, 75, 25);
2307 td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2308 HELPCTX(ssh_ttymodes),
2309 ttymodes_handler, P(td));
2310 td->modelist->generic.column = 0;
2311 td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2312 HELPCTX(ssh_ttymodes),
2313 ttymodes_handler, P(td));
2314 td->addbutton->generic.column = 1;
2315 td->addbutton->generic.tabdelay = 1;
2316 ctrl_columns(s, 1, 100); /* column break */
2317 /* Bit of a hack to get the value radio buttons and
2318 * edit-box on the same row. */
2319 ctrl_columns(s, 3, 25, 50, 25);
2320 c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2321 c->generic.column = 0;
2322 td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2323 HELPCTX(ssh_ttymodes),
2324 ttymodes_handler, P(td),
2325 "Auto", NO_SHORTCUT, P(NULL),
2326 "This:", NO_SHORTCUT, P(NULL),
2327 NULL);
2328 td->valradio->generic.column = 1;
2329 td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2330 HELPCTX(ssh_ttymodes),
2331 ttymodes_handler, P(td), P(NULL));
2332 td->valbox->generic.column = 2;
2333 ctrl_tabdelay(s, td->addbutton);
2334
2335 }
2336
2337 if (!midsession) {
2338 /*
2339 * The Connection/SSH/X11 panel.
2340 */
2341 ctrl_settitle(b, "Connection/SSH/X11",
2342 "Options controlling SSH X11 forwarding");
2343
2344 s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2345 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2346 HELPCTX(ssh_tunnels_x11),
2347 conf_checkbox_handler,I(CONF_x11_forward));
2348 ctrl_editbox(s, "X display location", 'x', 50,
2349 HELPCTX(ssh_tunnels_x11),
2350 conf_editbox_handler, I(CONF_x11_display), I(1));
2351 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2352 HELPCTX(ssh_tunnels_x11auth),
2353 conf_radiobutton_handler,
2354 I(CONF_x11_auth),
2355 "MIT-Magic-Cookie-1", I(X11_MIT),
2356 "XDM-Authorization-1", I(X11_XDM), NULL);
2357 }
2358
2359 /*
2360 * The Tunnels panel _is_ still available in mid-session.
2361 */
2362 ctrl_settitle(b, "Connection/SSH/Tunnels",
2363 "Options controlling SSH port forwarding");
2364
2365 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2366 "Port forwarding");
2367 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2368 HELPCTX(ssh_tunnels_portfwd_localhost),
2369 conf_checkbox_handler,
2370 I(CONF_lport_acceptall));
2371 ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2372 HELPCTX(ssh_tunnels_portfwd_localhost),
2373 conf_checkbox_handler,
2374 I(CONF_rport_acceptall));
2375
2376 ctrl_columns(s, 3, 55, 20, 25);
2377 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2378 c->generic.column = COLUMN_FIELD(0,2);
2379 /* You want to select from the list, _then_ hit Remove. So tab order
2380 * should be that way round. */
2381 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2382 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2383 HELPCTX(ssh_tunnels_portfwd),
2384 portfwd_handler, P(pfd));
2385 pfd->rembutton->generic.column = 2;
2386 pfd->rembutton->generic.tabdelay = 1;
2387 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2388 HELPCTX(ssh_tunnels_portfwd),
2389 portfwd_handler, P(pfd));
2390 pfd->listbox->listbox.height = 3;
2391 pfd->listbox->listbox.ncols = 2;
2392 pfd->listbox->listbox.percentages = snewn(2, int);
2393 pfd->listbox->listbox.percentages[0] = 20;
2394 pfd->listbox->listbox.percentages[1] = 80;
2395 ctrl_tabdelay(s, pfd->rembutton);
2396 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2397 /* You want to enter source, destination and type, _then_ hit Add.
2398 * Again, we adjust the tab order to reflect this. */
2399 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2400 HELPCTX(ssh_tunnels_portfwd),
2401 portfwd_handler, P(pfd));
2402 pfd->addbutton->generic.column = 2;
2403 pfd->addbutton->generic.tabdelay = 1;
2404 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2405 HELPCTX(ssh_tunnels_portfwd),
2406 portfwd_handler, P(pfd), P(NULL));
2407 pfd->sourcebox->generic.column = 0;
2408 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2409 HELPCTX(ssh_tunnels_portfwd),
2410 portfwd_handler, P(pfd), P(NULL));
2411 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2412 HELPCTX(ssh_tunnels_portfwd),
2413 portfwd_handler, P(pfd),
2414 "Local", 'l', P(NULL),
2415 "Remote", 'm', P(NULL),
2416 "Dynamic", 'y', P(NULL),
2417 NULL);
2418 #ifndef NO_IPV6
2419 pfd->addressfamily =
2420 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2421 HELPCTX(ssh_tunnels_portfwd_ipversion),
2422 portfwd_handler, P(pfd),
2423 "Auto", 'u', I(ADDRTYPE_UNSPEC),
2424 "IPv4", '4', I(ADDRTYPE_IPV4),
2425 "IPv6", '6', I(ADDRTYPE_IPV6),
2426 NULL);
2427 #endif
2428 ctrl_tabdelay(s, pfd->addbutton);
2429 ctrl_columns(s, 1, 100);
2430
2431 if (!midsession) {
2432 /*
2433 * The Connection/SSH/Bugs panel.
2434 */
2435 ctrl_settitle(b, "Connection/SSH/Bugs",
2436 "Workarounds for SSH server bugs");
2437
2438 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2439 "Detection of known bugs in SSH servers");
2440 ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2441 HELPCTX(ssh_bugs_ignore1),
2442 sshbug_handler, I(CONF_sshbug_ignore1));
2443 ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2444 HELPCTX(ssh_bugs_plainpw1),
2445 sshbug_handler, I(CONF_sshbug_plainpw1));
2446 ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2447 HELPCTX(ssh_bugs_rsa1),
2448 sshbug_handler, I(CONF_sshbug_rsa1));
2449 ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
2450 HELPCTX(ssh_bugs_ignore2),
2451 sshbug_handler, I(CONF_sshbug_ignore2));
2452 ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2453 HELPCTX(ssh_bugs_hmac2),
2454 sshbug_handler, I(CONF_sshbug_hmac2));
2455 ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2456 HELPCTX(ssh_bugs_derivekey2),
2457 sshbug_handler, I(CONF_sshbug_derivekey2));
2458 ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2459 HELPCTX(ssh_bugs_rsapad2),
2460 sshbug_handler, I(CONF_sshbug_rsapad2));
2461 ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2462 HELPCTX(ssh_bugs_pksessid2),
2463 sshbug_handler, I(CONF_sshbug_pksessid2));
2464 ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2465 HELPCTX(ssh_bugs_rekey2),
2466 sshbug_handler, I(CONF_sshbug_rekey2));
2467 ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
2468 HELPCTX(ssh_bugs_maxpkt2),
2469 sshbug_handler, I(CONF_sshbug_maxpkt2));
2470 }
2471 }
2472 }