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