Various SSH-related things were appearing in the PuTTYtel configuration dialog,
[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 /*
19 * Convenience function: determine whether this binary supports a
20 * given backend.
21 */
22 static int have_backend(int protocol)
23 {
24 struct backend_list *p = backends;
25 for (p = backends; p->name; p++) {
26 if (p->protocol == protocol)
27 return 1;
28 }
29 return 0;
30 }
31
32 static void config_host_handler(union control *ctrl, void *dlg,
33 void *data, int event)
34 {
35 Config *cfg = (Config *)data;
36
37 /*
38 * This function works just like the standard edit box handler,
39 * only it has to choose the control's label and text from two
40 * different places depending on the protocol.
41 */
42 if (event == EVENT_REFRESH) {
43 if (cfg->protocol == PROT_SERIAL) {
44 /*
45 * This label text is carefully chosen to contain an n,
46 * since that's the shortcut for the host name control.
47 */
48 dlg_label_change(ctrl, dlg, "Serial line");
49 dlg_editbox_set(ctrl, dlg, cfg->serline);
50 } else {
51 dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
52 dlg_editbox_set(ctrl, dlg, cfg->host);
53 }
54 } else if (event == EVENT_VALCHANGE) {
55 if (cfg->protocol == PROT_SERIAL)
56 dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline));
57 else
58 dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host));
59 }
60 }
61
62 static void config_port_handler(union control *ctrl, void *dlg,
63 void *data, int event)
64 {
65 Config *cfg = (Config *)data;
66 char buf[80];
67
68 /*
69 * This function works just like the standard edit box handler,
70 * only it has to choose the control's label and text from two
71 * different places depending on the protocol.
72 */
73 if (event == EVENT_REFRESH) {
74 if (cfg->protocol == PROT_SERIAL) {
75 /*
76 * This label text is carefully chosen to contain a p,
77 * since that's the shortcut for the port control.
78 */
79 dlg_label_change(ctrl, dlg, "Speed");
80 sprintf(buf, "%d", cfg->serspeed);
81 } else {
82 dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
83 sprintf(buf, "%d", cfg->port);
84 }
85 dlg_editbox_set(ctrl, dlg, buf);
86 } else if (event == EVENT_VALCHANGE) {
87 dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
88 if (cfg->protocol == PROT_SERIAL)
89 cfg->serspeed = atoi(buf);
90 else
91 cfg->port = atoi(buf);
92 }
93 }
94
95 struct hostport {
96 union control *host, *port;
97 };
98
99 /*
100 * We export this function so that platform-specific config
101 * routines can use it to conveniently identify the protocol radio
102 * buttons in order to add to them.
103 */
104 void config_protocolbuttons_handler(union control *ctrl, void *dlg,
105 void *data, int event)
106 {
107 int button, defport;
108 Config *cfg = (Config *)data;
109 struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
110
111 /*
112 * This function works just like the standard radio-button
113 * handler, except that it also has to change the setting of
114 * the port box, and refresh both host and port boxes when. We
115 * expect the context parameter to point at a hostport
116 * structure giving the `union control's for both.
117 */
118 if (event == EVENT_REFRESH) {
119 for (button = 0; button < ctrl->radio.nbuttons; button++)
120 if (cfg->protocol == ctrl->radio.buttondata[button].i)
121 break;
122 /* We expected that `break' to happen, in all circumstances. */
123 assert(button < ctrl->radio.nbuttons);
124 dlg_radiobutton_set(ctrl, dlg, button);
125 } else if (event == EVENT_VALCHANGE) {
126 int oldproto = cfg->protocol;
127 button = dlg_radiobutton_get(ctrl, dlg);
128 assert(button >= 0 && button < ctrl->radio.nbuttons);
129 cfg->protocol = ctrl->radio.buttondata[button].i;
130 if (oldproto != cfg->protocol) {
131 defport = -1;
132 switch (cfg->protocol) {
133 case PROT_SSH: defport = 22; break;
134 case PROT_TELNET: defport = 23; break;
135 case PROT_RLOGIN: defport = 513; break;
136 }
137 if (defport > 0 && cfg->port != defport) {
138 cfg->port = defport;
139 }
140 }
141 dlg_refresh(hp->host, dlg);
142 dlg_refresh(hp->port, dlg);
143 }
144 }
145
146 static void loggingbuttons_handler(union control *ctrl, void *dlg,
147 void *data, int event)
148 {
149 int button;
150 Config *cfg = (Config *)data;
151 /* This function works just like the standard radio-button handler,
152 * but it has to fall back to "no logging" in situations where the
153 * configured logging type isn't applicable.
154 */
155 if (event == EVENT_REFRESH) {
156 for (button = 0; button < ctrl->radio.nbuttons; button++)
157 if (cfg->logtype == ctrl->radio.buttondata[button].i)
158 break;
159
160 /* We fell off the end, so we lack the configured logging type */
161 if (button == ctrl->radio.nbuttons) {
162 button=0;
163 cfg->logtype=LGTYP_NONE;
164 }
165 dlg_radiobutton_set(ctrl, dlg, button);
166 } else if (event == EVENT_VALCHANGE) {
167 button = dlg_radiobutton_get(ctrl, dlg);
168 assert(button >= 0 && button < ctrl->radio.nbuttons);
169 cfg->logtype = ctrl->radio.buttondata[button].i;
170 }
171 }
172
173 static void numeric_keypad_handler(union control *ctrl, void *dlg,
174 void *data, int event)
175 {
176 int button;
177 Config *cfg = (Config *)data;
178 /*
179 * This function works much like the standard radio button
180 * handler, but it has to handle two fields in Config.
181 */
182 if (event == EVENT_REFRESH) {
183 if (cfg->nethack_keypad)
184 button = 2;
185 else if (cfg->app_keypad)
186 button = 1;
187 else
188 button = 0;
189 assert(button < ctrl->radio.nbuttons);
190 dlg_radiobutton_set(ctrl, dlg, button);
191 } else if (event == EVENT_VALCHANGE) {
192 button = dlg_radiobutton_get(ctrl, dlg);
193 assert(button >= 0 && button < ctrl->radio.nbuttons);
194 if (button == 2) {
195 cfg->app_keypad = FALSE;
196 cfg->nethack_keypad = TRUE;
197 } else {
198 cfg->app_keypad = (button != 0);
199 cfg->nethack_keypad = FALSE;
200 }
201 }
202 }
203
204 static void cipherlist_handler(union control *ctrl, void *dlg,
205 void *data, int event)
206 {
207 Config *cfg = (Config *)data;
208 if (event == EVENT_REFRESH) {
209 int i;
210
211 static const struct { char *s; int c; } ciphers[] = {
212 { "3DES", CIPHER_3DES },
213 { "Blowfish", CIPHER_BLOWFISH },
214 { "DES", CIPHER_DES },
215 { "AES (SSH-2 only)", CIPHER_AES },
216 { "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
217 { "-- warn below here --", CIPHER_WARN }
218 };
219
220 /* Set up the "selected ciphers" box. */
221 /* (cipherlist assumed to contain all ciphers) */
222 dlg_update_start(ctrl, dlg);
223 dlg_listbox_clear(ctrl, dlg);
224 for (i = 0; i < CIPHER_MAX; i++) {
225 int c = cfg->ssh_cipherlist[i];
226 int j;
227 char *cstr = NULL;
228 for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
229 if (ciphers[j].c == c) {
230 cstr = ciphers[j].s;
231 break;
232 }
233 }
234 dlg_listbox_addwithid(ctrl, dlg, cstr, c);
235 }
236 dlg_update_done(ctrl, dlg);
237
238 } else if (event == EVENT_VALCHANGE) {
239 int i;
240
241 /* Update array to match the list box. */
242 for (i=0; i < CIPHER_MAX; i++)
243 cfg->ssh_cipherlist[i] = dlg_listbox_getid(ctrl, dlg, i);
244
245 }
246 }
247
248 static void kexlist_handler(union control *ctrl, void *dlg,
249 void *data, int event)
250 {
251 Config *cfg = (Config *)data;
252 if (event == EVENT_REFRESH) {
253 int i;
254
255 static const struct { char *s; int k; } kexes[] = {
256 { "Diffie-Hellman group 1", KEX_DHGROUP1 },
257 { "Diffie-Hellman group 14", KEX_DHGROUP14 },
258 { "Diffie-Hellman group exchange", KEX_DHGEX },
259 { "-- warn below here --", KEX_WARN }
260 };
261
262 /* Set up the "kex preference" box. */
263 /* (kexlist assumed to contain all algorithms) */
264 dlg_update_start(ctrl, dlg);
265 dlg_listbox_clear(ctrl, dlg);
266 for (i = 0; i < KEX_MAX; i++) {
267 int k = cfg->ssh_kexlist[i];
268 int j;
269 char *kstr = NULL;
270 for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
271 if (kexes[j].k == k) {
272 kstr = kexes[j].s;
273 break;
274 }
275 }
276 dlg_listbox_addwithid(ctrl, dlg, kstr, k);
277 }
278 dlg_update_done(ctrl, dlg);
279
280 } else if (event == EVENT_VALCHANGE) {
281 int i;
282
283 /* Update array to match the list box. */
284 for (i=0; i < KEX_MAX; i++)
285 cfg->ssh_kexlist[i] = dlg_listbox_getid(ctrl, dlg, i);
286
287 }
288 }
289
290 static void printerbox_handler(union control *ctrl, void *dlg,
291 void *data, int event)
292 {
293 Config *cfg = (Config *)data;
294 if (event == EVENT_REFRESH) {
295 int nprinters, i;
296 printer_enum *pe;
297
298 dlg_update_start(ctrl, dlg);
299 /*
300 * Some backends may wish to disable the drop-down list on
301 * this edit box. Be prepared for this.
302 */
303 if (ctrl->editbox.has_list) {
304 dlg_listbox_clear(ctrl, dlg);
305 dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
306 pe = printer_start_enum(&nprinters);
307 for (i = 0; i < nprinters; i++)
308 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
309 printer_finish_enum(pe);
310 }
311 dlg_editbox_set(ctrl, dlg,
312 (*cfg->printer ? cfg->printer :
313 PRINTER_DISABLED_STRING));
314 dlg_update_done(ctrl, dlg);
315 } else if (event == EVENT_VALCHANGE) {
316 dlg_editbox_get(ctrl, dlg, cfg->printer, sizeof(cfg->printer));
317 if (!strcmp(cfg->printer, PRINTER_DISABLED_STRING))
318 *cfg->printer = '\0';
319 }
320 }
321
322 static void codepage_handler(union control *ctrl, void *dlg,
323 void *data, int event)
324 {
325 Config *cfg = (Config *)data;
326 if (event == EVENT_REFRESH) {
327 int i;
328 const char *cp;
329 dlg_update_start(ctrl, dlg);
330 strcpy(cfg->line_codepage,
331 cp_name(decode_codepage(cfg->line_codepage)));
332 dlg_listbox_clear(ctrl, dlg);
333 for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
334 dlg_listbox_add(ctrl, dlg, cp);
335 dlg_editbox_set(ctrl, dlg, cfg->line_codepage);
336 dlg_update_done(ctrl, dlg);
337 } else if (event == EVENT_VALCHANGE) {
338 dlg_editbox_get(ctrl, dlg, cfg->line_codepage,
339 sizeof(cfg->line_codepage));
340 strcpy(cfg->line_codepage,
341 cp_name(decode_codepage(cfg->line_codepage)));
342 }
343 }
344
345 static void sshbug_handler(union control *ctrl, void *dlg,
346 void *data, int event)
347 {
348 if (event == EVENT_REFRESH) {
349 dlg_update_start(ctrl, dlg);
350 dlg_listbox_clear(ctrl, dlg);
351 dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
352 dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
353 dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
354 switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) {
355 case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
356 case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
357 case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
358 }
359 dlg_update_done(ctrl, dlg);
360 } else if (event == EVENT_SELCHANGE) {
361 int i = dlg_listbox_index(ctrl, dlg);
362 if (i < 0)
363 i = AUTO;
364 else
365 i = dlg_listbox_getid(ctrl, dlg, i);
366 *(int *)ATOFFSET(data, ctrl->listbox.context.i) = i;
367 }
368 }
369
370 #define SAVEDSESSION_LEN 2048
371
372 struct sessionsaver_data {
373 union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
374 union control *okbutton, *cancelbutton;
375 struct sesslist sesslist;
376 int midsession;
377 };
378
379 /*
380 * Helper function to load the session selected in the list box, if
381 * any, as this is done in more than one place below. Returns 0 for
382 * failure.
383 */
384 static int load_selected_session(struct sessionsaver_data *ssd,
385 char *savedsession,
386 void *dlg, Config *cfg)
387 {
388 int i = dlg_listbox_index(ssd->listbox, dlg);
389 int isdef;
390 if (i < 0) {
391 dlg_beep(dlg);
392 return 0;
393 }
394 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
395 load_settings(ssd->sesslist.sessions[i], !isdef, cfg);
396 if (!isdef) {
397 strncpy(savedsession, ssd->sesslist.sessions[i],
398 SAVEDSESSION_LEN);
399 savedsession[SAVEDSESSION_LEN-1] = '\0';
400 } else {
401 savedsession[0] = '\0';
402 }
403 dlg_refresh(NULL, dlg);
404 /* Restore the selection, which might have been clobbered by
405 * changing the value of the edit box. */
406 dlg_listbox_select(ssd->listbox, dlg, i);
407 return 1;
408 }
409
410 static void sessionsaver_handler(union control *ctrl, void *dlg,
411 void *data, int event)
412 {
413 Config *cfg = (Config *)data;
414 struct sessionsaver_data *ssd =
415 (struct sessionsaver_data *)ctrl->generic.context.p;
416 char *savedsession;
417
418 /*
419 * The first time we're called in a new dialog, we must
420 * allocate space to store the current contents of the saved
421 * session edit box (since it must persist even when we switch
422 * panels, but is not part of the Config).
423 */
424 if (!ssd->editbox) {
425 savedsession = NULL;
426 } else if (!dlg_get_privdata(ssd->editbox, dlg)) {
427 savedsession = (char *)
428 dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN);
429 savedsession[0] = '\0';
430 } else {
431 savedsession = dlg_get_privdata(ssd->editbox, dlg);
432 }
433
434 if (event == EVENT_REFRESH) {
435 if (ctrl == ssd->editbox) {
436 dlg_editbox_set(ctrl, dlg, savedsession);
437 } else if (ctrl == ssd->listbox) {
438 int i;
439 dlg_update_start(ctrl, dlg);
440 dlg_listbox_clear(ctrl, dlg);
441 for (i = 0; i < ssd->sesslist.nsessions; i++)
442 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
443 dlg_update_done(ctrl, dlg);
444 }
445 } else if (event == EVENT_VALCHANGE) {
446 int top, bottom, halfway, i;
447 if (ctrl == ssd->editbox) {
448 dlg_editbox_get(ctrl, dlg, savedsession,
449 SAVEDSESSION_LEN);
450 top = ssd->sesslist.nsessions;
451 bottom = -1;
452 while (top-bottom > 1) {
453 halfway = (top+bottom)/2;
454 i = strcmp(savedsession, ssd->sesslist.sessions[halfway]);
455 if (i <= 0 ) {
456 top = halfway;
457 } else {
458 bottom = halfway;
459 }
460 }
461 if (top == ssd->sesslist.nsessions) {
462 top -= 1;
463 }
464 dlg_listbox_select(ssd->listbox, dlg, top);
465 }
466 } else if (event == EVENT_ACTION) {
467 if (!ssd->midsession &&
468 (ctrl == ssd->listbox ||
469 (ssd->loadbutton && ctrl == ssd->loadbutton))) {
470 /*
471 * The user has double-clicked a session, or hit Load.
472 * We must load the selected session, and then
473 * terminate the configuration dialog _if_ there was a
474 * double-click on the list box _and_ that session
475 * contains a hostname.
476 */
477 if (load_selected_session(ssd, savedsession, dlg, cfg) &&
478 (ctrl == ssd->listbox && cfg_launchable(cfg))) {
479 dlg_end(dlg, 1); /* it's all over, and succeeded */
480 }
481 } else if (ctrl == ssd->savebutton) {
482 int isdef = !strcmp(savedsession, "Default Settings");
483 if (!savedsession[0]) {
484 int i = dlg_listbox_index(ssd->listbox, dlg);
485 if (i < 0) {
486 dlg_beep(dlg);
487 return;
488 }
489 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
490 if (!isdef) {
491 strncpy(savedsession, ssd->sesslist.sessions[i],
492 SAVEDSESSION_LEN);
493 savedsession[SAVEDSESSION_LEN-1] = '\0';
494 } else {
495 savedsession[0] = '\0';
496 }
497 }
498 {
499 char *errmsg = save_settings(savedsession, !isdef, cfg);
500 if (errmsg) {
501 dlg_error_msg(dlg, errmsg);
502 sfree(errmsg);
503 }
504 }
505 get_sesslist(&ssd->sesslist, FALSE);
506 get_sesslist(&ssd->sesslist, TRUE);
507 dlg_refresh(ssd->editbox, dlg);
508 dlg_refresh(ssd->listbox, dlg);
509 } else if (!ssd->midsession &&
510 ssd->delbutton && ctrl == ssd->delbutton) {
511 int i = dlg_listbox_index(ssd->listbox, dlg);
512 if (i <= 0) {
513 dlg_beep(dlg);
514 } else {
515 del_settings(ssd->sesslist.sessions[i]);
516 get_sesslist(&ssd->sesslist, FALSE);
517 get_sesslist(&ssd->sesslist, TRUE);
518 dlg_refresh(ssd->listbox, dlg);
519 }
520 } else if (ctrl == ssd->okbutton) {
521 if (ssd->midsession) {
522 /* In a mid-session Change Settings, Apply is always OK. */
523 dlg_end(dlg, 1);
524 return;
525 }
526 /*
527 * Annoying special case. If the `Open' button is
528 * pressed while no host name is currently set, _and_
529 * the session list previously had the focus, _and_
530 * there was a session selected in that which had a
531 * valid host name in it, then load it and go.
532 */
533 if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
534 !cfg_launchable(cfg)) {
535 Config cfg2;
536 if (!load_selected_session(ssd, savedsession, dlg, &cfg2)) {
537 dlg_beep(dlg);
538 return;
539 }
540 /* If at this point we have a valid session, go! */
541 if (*cfg2.host) {
542 *cfg = cfg2; /* structure copy */
543 cfg->remote_cmd_ptr = NULL;
544 dlg_end(dlg, 1);
545 } else
546 dlg_beep(dlg);
547 return;
548 }
549
550 /*
551 * Otherwise, do the normal thing: if we have a valid
552 * session, get going.
553 */
554 if (cfg_launchable(cfg)) {
555 dlg_end(dlg, 1);
556 } else
557 dlg_beep(dlg);
558 } else if (ctrl == ssd->cancelbutton) {
559 dlg_end(dlg, 0);
560 }
561 }
562 }
563
564 struct charclass_data {
565 union control *listbox, *editbox, *button;
566 };
567
568 static void charclass_handler(union control *ctrl, void *dlg,
569 void *data, int event)
570 {
571 Config *cfg = (Config *)data;
572 struct charclass_data *ccd =
573 (struct charclass_data *)ctrl->generic.context.p;
574
575 if (event == EVENT_REFRESH) {
576 if (ctrl == ccd->listbox) {
577 int i;
578 dlg_update_start(ctrl, dlg);
579 dlg_listbox_clear(ctrl, dlg);
580 for (i = 0; i < 128; i++) {
581 char str[100];
582 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
583 (i >= 0x21 && i != 0x7F) ? i : ' ', cfg->wordness[i]);
584 dlg_listbox_add(ctrl, dlg, str);
585 }
586 dlg_update_done(ctrl, dlg);
587 }
588 } else if (event == EVENT_ACTION) {
589 if (ctrl == ccd->button) {
590 char str[100];
591 int i, n;
592 dlg_editbox_get(ccd->editbox, dlg, str, sizeof(str));
593 n = atoi(str);
594 for (i = 0; i < 128; i++) {
595 if (dlg_listbox_issel(ccd->listbox, dlg, i))
596 cfg->wordness[i] = n;
597 }
598 dlg_refresh(ccd->listbox, dlg);
599 }
600 }
601 }
602
603 struct colour_data {
604 union control *listbox, *redit, *gedit, *bedit, *button;
605 };
606
607 static const char *const colours[] = {
608 "Default Foreground", "Default Bold Foreground",
609 "Default Background", "Default Bold Background",
610 "Cursor Text", "Cursor Colour",
611 "ANSI Black", "ANSI Black Bold",
612 "ANSI Red", "ANSI Red Bold",
613 "ANSI Green", "ANSI Green Bold",
614 "ANSI Yellow", "ANSI Yellow Bold",
615 "ANSI Blue", "ANSI Blue Bold",
616 "ANSI Magenta", "ANSI Magenta Bold",
617 "ANSI Cyan", "ANSI Cyan Bold",
618 "ANSI White", "ANSI White Bold"
619 };
620
621 static void colour_handler(union control *ctrl, void *dlg,
622 void *data, int event)
623 {
624 Config *cfg = (Config *)data;
625 struct colour_data *cd =
626 (struct colour_data *)ctrl->generic.context.p;
627 int update = FALSE, r, g, b;
628
629 if (event == EVENT_REFRESH) {
630 if (ctrl == cd->listbox) {
631 int i;
632 dlg_update_start(ctrl, dlg);
633 dlg_listbox_clear(ctrl, dlg);
634 for (i = 0; i < lenof(colours); i++)
635 dlg_listbox_add(ctrl, dlg, colours[i]);
636 dlg_update_done(ctrl, dlg);
637 dlg_editbox_set(cd->redit, dlg, "");
638 dlg_editbox_set(cd->gedit, dlg, "");
639 dlg_editbox_set(cd->bedit, dlg, "");
640 }
641 } else if (event == EVENT_SELCHANGE) {
642 if (ctrl == cd->listbox) {
643 /* The user has selected a colour. Update the RGB text. */
644 int i = dlg_listbox_index(ctrl, dlg);
645 if (i < 0) {
646 dlg_beep(dlg);
647 return;
648 }
649 r = cfg->colours[i][0];
650 g = cfg->colours[i][1];
651 b = cfg->colours[i][2];
652 update = TRUE;
653 }
654 } else if (event == EVENT_VALCHANGE) {
655 if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
656 /* The user has changed the colour using the edit boxes. */
657 char buf[80];
658 int i, cval;
659
660 dlg_editbox_get(ctrl, dlg, buf, lenof(buf));
661 cval = atoi(buf);
662 if (cval > 255) cval = 255;
663 if (cval < 0) cval = 0;
664
665 i = dlg_listbox_index(cd->listbox, dlg);
666 if (i >= 0) {
667 if (ctrl == cd->redit)
668 cfg->colours[i][0] = cval;
669 else if (ctrl == cd->gedit)
670 cfg->colours[i][1] = cval;
671 else if (ctrl == cd->bedit)
672 cfg->colours[i][2] = cval;
673 }
674 }
675 } else if (event == EVENT_ACTION) {
676 if (ctrl == cd->button) {
677 int i = dlg_listbox_index(cd->listbox, dlg);
678 if (i < 0) {
679 dlg_beep(dlg);
680 return;
681 }
682 /*
683 * Start a colour selector, which will send us an
684 * EVENT_CALLBACK when it's finished and allow us to
685 * pick up the results.
686 */
687 dlg_coloursel_start(ctrl, dlg,
688 cfg->colours[i][0],
689 cfg->colours[i][1],
690 cfg->colours[i][2]);
691 }
692 } else if (event == EVENT_CALLBACK) {
693 if (ctrl == cd->button) {
694 int i = dlg_listbox_index(cd->listbox, dlg);
695 /*
696 * Collect the results of the colour selector. Will
697 * return nonzero on success, or zero if the colour
698 * selector did nothing (user hit Cancel, for example).
699 */
700 if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
701 cfg->colours[i][0] = r;
702 cfg->colours[i][1] = g;
703 cfg->colours[i][2] = b;
704 update = TRUE;
705 }
706 }
707 }
708
709 if (update) {
710 char buf[40];
711 sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
712 sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
713 sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
714 }
715 }
716
717 struct ttymodes_data {
718 union control *modelist, *valradio, *valbox;
719 union control *addbutton, *rembutton, *listbox;
720 };
721
722 static void ttymodes_handler(union control *ctrl, void *dlg,
723 void *data, int event)
724 {
725 Config *cfg = (Config *)data;
726 struct ttymodes_data *td =
727 (struct ttymodes_data *)ctrl->generic.context.p;
728
729 if (event == EVENT_REFRESH) {
730 if (ctrl == td->listbox) {
731 char *p = cfg->ttymodes;
732 dlg_update_start(ctrl, dlg);
733 dlg_listbox_clear(ctrl, dlg);
734 while (*p) {
735 int tabpos = strchr(p, '\t') - p;
736 char *disp = dupprintf("%.*s\t%s", tabpos, p,
737 (p[tabpos+1] == 'A') ? "(auto)" :
738 p+tabpos+2);
739 dlg_listbox_add(ctrl, dlg, disp);
740 p += strlen(p) + 1;
741 sfree(disp);
742 }
743 dlg_update_done(ctrl, dlg);
744 } else if (ctrl == td->modelist) {
745 int i;
746 dlg_update_start(ctrl, dlg);
747 dlg_listbox_clear(ctrl, dlg);
748 for (i = 0; ttymodes[i]; i++)
749 dlg_listbox_add(ctrl, dlg, ttymodes[i]);
750 dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
751 dlg_update_done(ctrl, dlg);
752 } else if (ctrl == td->valradio) {
753 dlg_radiobutton_set(ctrl, dlg, 0);
754 }
755 } else if (event == EVENT_ACTION) {
756 if (ctrl == td->addbutton) {
757 int ind = dlg_listbox_index(td->modelist, dlg);
758 if (ind >= 0) {
759 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
760 int slen, left;
761 char *p, str[lenof(cfg->ttymodes)];
762 /* Construct new entry */
763 memset(str, 0, lenof(str));
764 strncpy(str, ttymodes[ind], lenof(str)-3);
765 slen = strlen(str);
766 str[slen] = '\t';
767 str[slen+1] = type;
768 slen += 2;
769 if (type == 'V') {
770 dlg_editbox_get(td->valbox, dlg, str+slen, lenof(str)-slen);
771 }
772 /* Find end of list, deleting any existing instance */
773 p = cfg->ttymodes;
774 left = lenof(cfg->ttymodes);
775 while (*p) {
776 int t = strchr(p, '\t') - p;
777 if (t == strlen(ttymodes[ind]) &&
778 strncmp(p, ttymodes[ind], t) == 0) {
779 memmove(p, p+strlen(p)+1, left - (strlen(p)+1));
780 continue;
781 }
782 left -= strlen(p) + 1;
783 p += strlen(p) + 1;
784 }
785 /* Append new entry */
786 memset(p, 0, left);
787 strncpy(p, str, left - 2);
788 dlg_refresh(td->listbox, dlg);
789 } else
790 dlg_beep(dlg);
791 } else if (ctrl == td->rembutton) {
792 char *p = cfg->ttymodes;
793 int i = 0, len = lenof(cfg->ttymodes);
794 while (*p) {
795 if (dlg_listbox_issel(td->listbox, dlg, i)) {
796 memmove(p, p+strlen(p)+1, len - (strlen(p)+1));
797 i++;
798 continue;
799 }
800 len -= strlen(p) + 1;
801 p += strlen(p) + 1;
802 i++;
803 }
804 memset(p, 0, lenof(cfg->ttymodes) - len);
805 dlg_refresh(td->listbox, dlg);
806 }
807 }
808 }
809
810 struct environ_data {
811 union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
812 };
813
814 static void environ_handler(union control *ctrl, void *dlg,
815 void *data, int event)
816 {
817 Config *cfg = (Config *)data;
818 struct environ_data *ed =
819 (struct environ_data *)ctrl->generic.context.p;
820
821 if (event == EVENT_REFRESH) {
822 if (ctrl == ed->listbox) {
823 char *p = cfg->environmt;
824 dlg_update_start(ctrl, dlg);
825 dlg_listbox_clear(ctrl, dlg);
826 while (*p) {
827 dlg_listbox_add(ctrl, dlg, p);
828 p += strlen(p) + 1;
829 }
830 dlg_update_done(ctrl, dlg);
831 }
832 } else if (event == EVENT_ACTION) {
833 if (ctrl == ed->addbutton) {
834 char str[sizeof(cfg->environmt)];
835 char *p;
836 dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1);
837 if (!*str) {
838 dlg_beep(dlg);
839 return;
840 }
841 p = str + strlen(str);
842 *p++ = '\t';
843 dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str));
844 if (!*p) {
845 dlg_beep(dlg);
846 return;
847 }
848 p = cfg->environmt;
849 while (*p) {
850 while (*p)
851 p++;
852 p++;
853 }
854 if ((p - cfg->environmt) + strlen(str) + 2 <
855 sizeof(cfg->environmt)) {
856 strcpy(p, str);
857 p[strlen(str) + 1] = '\0';
858 dlg_listbox_add(ed->listbox, dlg, str);
859 dlg_editbox_set(ed->varbox, dlg, "");
860 dlg_editbox_set(ed->valbox, dlg, "");
861 } else {
862 dlg_error_msg(dlg, "Environment too big");
863 }
864 } else if (ctrl == ed->rembutton) {
865 int i = dlg_listbox_index(ed->listbox, dlg);
866 if (i < 0) {
867 dlg_beep(dlg);
868 } else {
869 char *p, *q;
870
871 dlg_listbox_del(ed->listbox, dlg, i);
872 p = cfg->environmt;
873 while (i > 0) {
874 if (!*p)
875 goto disaster;
876 while (*p)
877 p++;
878 p++;
879 i--;
880 }
881 q = p;
882 if (!*p)
883 goto disaster;
884 while (*p)
885 p++;
886 p++;
887 while (*p) {
888 while (*p)
889 *q++ = *p++;
890 *q++ = *p++;
891 }
892 *q = '\0';
893 disaster:;
894 }
895 }
896 }
897 }
898
899 struct portfwd_data {
900 union control *addbutton, *rembutton, *listbox;
901 union control *sourcebox, *destbox, *direction;
902 #ifndef NO_IPV6
903 union control *addressfamily;
904 #endif
905 };
906
907 static void portfwd_handler(union control *ctrl, void *dlg,
908 void *data, int event)
909 {
910 Config *cfg = (Config *)data;
911 struct portfwd_data *pfd =
912 (struct portfwd_data *)ctrl->generic.context.p;
913
914 if (event == EVENT_REFRESH) {
915 if (ctrl == pfd->listbox) {
916 char *p = cfg->portfwd;
917 dlg_update_start(ctrl, dlg);
918 dlg_listbox_clear(ctrl, dlg);
919 while (*p) {
920 dlg_listbox_add(ctrl, dlg, p);
921 p += strlen(p) + 1;
922 }
923 dlg_update_done(ctrl, dlg);
924 } else if (ctrl == pfd->direction) {
925 /*
926 * Default is Local.
927 */
928 dlg_radiobutton_set(ctrl, dlg, 0);
929 #ifndef NO_IPV6
930 } else if (ctrl == pfd->addressfamily) {
931 dlg_radiobutton_set(ctrl, dlg, 0);
932 #endif
933 }
934 } else if (event == EVENT_ACTION) {
935 if (ctrl == pfd->addbutton) {
936 char str[sizeof(cfg->portfwd)];
937 char *p;
938 int i, type;
939 int whichbutton;
940
941 i = 0;
942 #ifndef NO_IPV6
943 whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
944 if (whichbutton == 1)
945 str[i++] = '4';
946 else if (whichbutton == 2)
947 str[i++] = '6';
948 #endif
949
950 whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
951 if (whichbutton == 0)
952 type = 'L';
953 else if (whichbutton == 1)
954 type = 'R';
955 else
956 type = 'D';
957 str[i++] = type;
958
959 dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i);
960 if (!str[i]) {
961 dlg_error_msg(dlg, "You need to specify a source port number");
962 return;
963 }
964 p = str + strlen(str);
965 if (type != 'D') {
966 *p++ = '\t';
967 dlg_editbox_get(pfd->destbox, dlg, p,
968 sizeof(str) - (p - str));
969 if (!*p || !strchr(p, ':')) {
970 dlg_error_msg(dlg,
971 "You need to specify a destination address\n"
972 "in the form \"host.name:port\"");
973 return;
974 }
975 } else
976 *p = '\0';
977 p = cfg->portfwd;
978 while (*p) {
979 while (*p)
980 p++;
981 p++;
982 }
983 if ((p - cfg->portfwd) + strlen(str) + 2 <=
984 sizeof(cfg->portfwd)) {
985 strcpy(p, str);
986 p[strlen(str) + 1] = '\0';
987 dlg_listbox_add(pfd->listbox, dlg, str);
988 dlg_editbox_set(pfd->sourcebox, dlg, "");
989 dlg_editbox_set(pfd->destbox, dlg, "");
990 } else {
991 dlg_error_msg(dlg, "Too many forwardings");
992 }
993 } else if (ctrl == pfd->rembutton) {
994 int i = dlg_listbox_index(pfd->listbox, dlg);
995 if (i < 0)
996 dlg_beep(dlg);
997 else {
998 char *p, *q;
999
1000 dlg_listbox_del(pfd->listbox, dlg, i);
1001 p = cfg->portfwd;
1002 while (i > 0) {
1003 if (!*p)
1004 goto disaster2;
1005 while (*p)
1006 p++;
1007 p++;
1008 i--;
1009 }
1010 q = p;
1011 if (!*p)
1012 goto disaster2;
1013 while (*p)
1014 p++;
1015 p++;
1016 while (*p) {
1017 while (*p)
1018 *q++ = *p++;
1019 *q++ = *p++;
1020 }
1021 *q = '\0';
1022 disaster2:;
1023 }
1024 }
1025 }
1026 }
1027
1028 void setup_config_box(struct controlbox *b, int midsession,
1029 int protocol, int protcfginfo)
1030 {
1031 struct controlset *s;
1032 struct sessionsaver_data *ssd;
1033 struct charclass_data *ccd;
1034 struct colour_data *cd;
1035 struct ttymodes_data *td;
1036 struct environ_data *ed;
1037 struct portfwd_data *pfd;
1038 union control *c;
1039 char *str;
1040
1041 ssd = (struct sessionsaver_data *)
1042 ctrl_alloc(b, sizeof(struct sessionsaver_data));
1043 memset(ssd, 0, sizeof(*ssd));
1044 ssd->midsession = midsession;
1045
1046 /*
1047 * The standard panel that appears at the bottom of all panels:
1048 * Open, Cancel, Apply etc.
1049 */
1050 s = ctrl_getset(b, "", "", "");
1051 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
1052 ssd->okbutton = ctrl_pushbutton(s,
1053 (midsession ? "Apply" : "Open"),
1054 (char)(midsession ? 'a' : 'o'),
1055 HELPCTX(no_help),
1056 sessionsaver_handler, P(ssd));
1057 ssd->okbutton->button.isdefault = TRUE;
1058 ssd->okbutton->generic.column = 3;
1059 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
1060 sessionsaver_handler, P(ssd));
1061 ssd->cancelbutton->button.iscancel = TRUE;
1062 ssd->cancelbutton->generic.column = 4;
1063 /* We carefully don't close the 5-column part, so that platform-
1064 * specific add-ons can put extra buttons alongside Open and Cancel. */
1065
1066 /*
1067 * The Session panel.
1068 */
1069 str = dupprintf("Basic options for your %s session", appname);
1070 ctrl_settitle(b, "Session", str);
1071 sfree(str);
1072
1073 if (!midsession) {
1074 struct hostport *hp = (struct hostport *)
1075 ctrl_alloc(b, sizeof(struct hostport));
1076
1077 s = ctrl_getset(b, "Session", "hostport",
1078 "Specify the destination you want to connect to");
1079 ctrl_columns(s, 2, 75, 25);
1080 c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
1081 HELPCTX(session_hostname),
1082 config_host_handler, I(0), I(0));
1083 c->generic.column = 0;
1084 hp->host = c;
1085 c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
1086 HELPCTX(session_hostname),
1087 config_port_handler, I(0), I(0));
1088 c->generic.column = 1;
1089 hp->port = c;
1090 ctrl_columns(s, 1, 100);
1091
1092 if (!have_backend(PROT_SSH)) {
1093 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,
1094 HELPCTX(session_hostname),
1095 config_protocolbuttons_handler, P(hp),
1096 "Raw", 'r', I(PROT_RAW),
1097 "Telnet", 't', I(PROT_TELNET),
1098 "Rlogin", 'i', I(PROT_RLOGIN),
1099 NULL);
1100 } else {
1101 ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,
1102 HELPCTX(session_hostname),
1103 config_protocolbuttons_handler, P(hp),
1104 "Raw", 'r', I(PROT_RAW),
1105 "Telnet", 't', I(PROT_TELNET),
1106 "Rlogin", 'i', I(PROT_RLOGIN),
1107 "SSH", 's', I(PROT_SSH),
1108 NULL);
1109 }
1110 }
1111
1112 /*
1113 * The Load/Save panel is available even in mid-session.
1114 */
1115 s = ctrl_getset(b, "Session", "savedsessions",
1116 midsession ? "Save the current session settings" :
1117 "Load, save or delete a stored session");
1118 ctrl_columns(s, 2, 75, 25);
1119 get_sesslist(&ssd->sesslist, TRUE);
1120 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
1121 HELPCTX(session_saved),
1122 sessionsaver_handler, P(ssd), P(NULL));
1123 ssd->editbox->generic.column = 0;
1124 /* Reset columns so that the buttons are alongside the list, rather
1125 * than alongside that edit box. */
1126 ctrl_columns(s, 1, 100);
1127 ctrl_columns(s, 2, 75, 25);
1128 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1129 HELPCTX(session_saved),
1130 sessionsaver_handler, P(ssd));
1131 ssd->listbox->generic.column = 0;
1132 ssd->listbox->listbox.height = 7;
1133 if (!midsession) {
1134 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
1135 HELPCTX(session_saved),
1136 sessionsaver_handler, P(ssd));
1137 ssd->loadbutton->generic.column = 1;
1138 } else {
1139 /* We can't offer the Load button mid-session, as it would allow the
1140 * user to load and subsequently save settings they can't see. (And
1141 * also change otherwise immutable settings underfoot; that probably
1142 * shouldn't be a problem, but.) */
1143 ssd->loadbutton = NULL;
1144 }
1145 /* "Save" button is permitted mid-session. */
1146 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
1147 HELPCTX(session_saved),
1148 sessionsaver_handler, P(ssd));
1149 ssd->savebutton->generic.column = 1;
1150 if (!midsession) {
1151 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
1152 HELPCTX(session_saved),
1153 sessionsaver_handler, P(ssd));
1154 ssd->delbutton->generic.column = 1;
1155 } else {
1156 /* Disable the Delete button mid-session too, for UI consistency. */
1157 ssd->delbutton = NULL;
1158 }
1159 ctrl_columns(s, 1, 100);
1160
1161 s = ctrl_getset(b, "Session", "otheropts", NULL);
1162 c = ctrl_radiobuttons(s, "Close window on exit:", 'w', 4,
1163 HELPCTX(session_coe),
1164 dlg_stdradiobutton_handler,
1165 I(offsetof(Config, close_on_exit)),
1166 "Always", I(FORCE_ON),
1167 "Never", I(FORCE_OFF),
1168 "Only on clean exit", I(AUTO), NULL);
1169
1170 /*
1171 * The Session/Logging panel.
1172 */
1173 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
1174
1175 s = ctrl_getset(b, "Session/Logging", "main", NULL);
1176 /*
1177 * The logging buttons change depending on whether SSH packet
1178 * logging can sensibly be available.
1179 */
1180 {
1181 char *sshlogname, *sshrawlogname;
1182 if ((midsession && protocol == PROT_SSH) ||
1183 (!midsession && have_backend(PROT_SSH))) {
1184 sshlogname = "SSH packets";
1185 sshrawlogname = "SSH packets and raw data";
1186 } else {
1187 sshlogname = NULL; /* this will disable both buttons */
1188 sshrawlogname = NULL; /* this will just placate optimisers */
1189 }
1190 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
1191 HELPCTX(logging_main),
1192 loggingbuttons_handler,
1193 I(offsetof(Config, logtype)),
1194 "None", 't', I(LGTYP_NONE),
1195 "Printable output", 'p', I(LGTYP_ASCII),
1196 "All session output", 'l', I(LGTYP_DEBUG),
1197 sshlogname, 's', I(LGTYP_PACKETS),
1198 sshrawlogname, 'r', I(LGTYP_SSHRAW),
1199 NULL);
1200 }
1201 ctrl_filesel(s, "Log file name:", 'f',
1202 NULL, TRUE, "Select session log file name",
1203 HELPCTX(logging_filename),
1204 dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));
1205 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
1206 " &T for time, and &H for host name)",
1207 HELPCTX(logging_filename));
1208 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
1209 HELPCTX(logging_exists),
1210 dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),
1211 "Always overwrite it", I(LGXF_OVR),
1212 "Always append to the end of it", I(LGXF_APN),
1213 "Ask the user every time", I(LGXF_ASK), NULL);
1214 ctrl_checkbox(s, "Flush log file frequently", 'u',
1215 HELPCTX(logging_flush),
1216 dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));
1217
1218 if ((midsession && protocol == PROT_SSH) ||
1219 (!midsession && have_backend(PROT_SSH))) {
1220 s = ctrl_getset(b, "Session/Logging", "ssh",
1221 "Options specific to SSH packet logging");
1222 ctrl_checkbox(s, "Omit known password fields", 'k',
1223 HELPCTX(logging_ssh_omit_password),
1224 dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass)));
1225 ctrl_checkbox(s, "Omit session data", 'd',
1226 HELPCTX(logging_ssh_omit_data),
1227 dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata)));
1228 }
1229
1230 /*
1231 * The Terminal panel.
1232 */
1233 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
1234
1235 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
1236 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
1237 HELPCTX(terminal_autowrap),
1238 dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));
1239 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
1240 HELPCTX(terminal_decom),
1241 dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));
1242 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
1243 HELPCTX(terminal_lfhascr),
1244 dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
1245 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
1246 HELPCTX(terminal_bce),
1247 dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
1248 ctrl_checkbox(s, "Enable blinking text", 'n',
1249 HELPCTX(terminal_blink),
1250 dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));
1251 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
1252 HELPCTX(terminal_answerback),
1253 dlg_stdeditbox_handler, I(offsetof(Config,answerback)),
1254 I(sizeof(((Config *)0)->answerback)));
1255
1256 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
1257 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
1258 HELPCTX(terminal_localecho),
1259 dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),
1260 "Auto", I(AUTO),
1261 "Force on", I(FORCE_ON),
1262 "Force off", I(FORCE_OFF), NULL);
1263 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
1264 HELPCTX(terminal_localedit),
1265 dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),
1266 "Auto", I(AUTO),
1267 "Force on", I(FORCE_ON),
1268 "Force off", I(FORCE_OFF), NULL);
1269
1270 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
1271 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
1272 HELPCTX(terminal_printing),
1273 printerbox_handler, P(NULL), P(NULL));
1274
1275 /*
1276 * The Terminal/Keyboard panel.
1277 */
1278 ctrl_settitle(b, "Terminal/Keyboard",
1279 "Options controlling the effects of keys");
1280
1281 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
1282 "Change the sequences sent by:");
1283 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
1284 HELPCTX(keyboard_backspace),
1285 dlg_stdradiobutton_handler,
1286 I(offsetof(Config, bksp_is_delete)),
1287 "Control-H", I(0), "Control-? (127)", I(1), NULL);
1288 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
1289 HELPCTX(keyboard_homeend),
1290 dlg_stdradiobutton_handler,
1291 I(offsetof(Config, rxvt_homeend)),
1292 "Standard", I(0), "rxvt", I(1), NULL);
1293 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
1294 HELPCTX(keyboard_funkeys),
1295 dlg_stdradiobutton_handler,
1296 I(offsetof(Config, funky_type)),
1297 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
1298 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
1299
1300 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
1301 "Application keypad settings:");
1302 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
1303 HELPCTX(keyboard_appcursor),
1304 dlg_stdradiobutton_handler,
1305 I(offsetof(Config, app_cursor)),
1306 "Normal", I(0), "Application", I(1), NULL);
1307 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
1308 HELPCTX(keyboard_appkeypad),
1309 numeric_keypad_handler, P(NULL),
1310 "Normal", I(0), "Application", I(1), "NetHack", I(2),
1311 NULL);
1312
1313 /*
1314 * The Terminal/Bell panel.
1315 */
1316 ctrl_settitle(b, "Terminal/Bell",
1317 "Options controlling the terminal bell");
1318
1319 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
1320 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
1321 HELPCTX(bell_style),
1322 dlg_stdradiobutton_handler, I(offsetof(Config, beep)),
1323 "None (bell disabled)", I(BELL_DISABLED),
1324 "Make default system alert sound", I(BELL_DEFAULT),
1325 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
1326
1327 s = ctrl_getset(b, "Terminal/Bell", "overload",
1328 "Control the bell overload behaviour");
1329 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
1330 HELPCTX(bell_overload),
1331 dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));
1332 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
1333 HELPCTX(bell_overload),
1334 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));
1335 ctrl_editbox(s, "... in this many seconds", 't', 20,
1336 HELPCTX(bell_overload),
1337 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
1338 I(-TICKSPERSEC));
1339 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
1340 HELPCTX(bell_overload));
1341 ctrl_editbox(s, "Seconds of silence required", 's', 20,
1342 HELPCTX(bell_overload),
1343 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
1344 I(-TICKSPERSEC));
1345
1346 /*
1347 * The Terminal/Features panel.
1348 */
1349 ctrl_settitle(b, "Terminal/Features",
1350 "Enabling and disabling advanced terminal features");
1351
1352 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
1353 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
1354 HELPCTX(features_application),
1355 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));
1356 ctrl_checkbox(s, "Disable application keypad mode", 'k',
1357 HELPCTX(features_application),
1358 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));
1359 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
1360 HELPCTX(features_mouse),
1361 dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));
1362 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
1363 HELPCTX(features_resize),
1364 dlg_stdcheckbox_handler,
1365 I(offsetof(Config,no_remote_resize)));
1366 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
1367 HELPCTX(features_altscreen),
1368 dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));
1369 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1370 HELPCTX(features_retitle),
1371 dlg_stdcheckbox_handler,
1372 I(offsetof(Config,no_remote_wintitle)));
1373 ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
1374 HELPCTX(features_qtitle),
1375 dlg_stdradiobutton_handler,
1376 I(offsetof(Config,remote_qtitle_action)),
1377 "None", I(TITLE_NONE),
1378 "Empty string", I(TITLE_EMPTY),
1379 "Window title", I(TITLE_REAL), NULL);
1380 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1381 HELPCTX(features_dbackspace),
1382 dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
1383 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1384 'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
1385 I(offsetof(Config,no_remote_charset)));
1386 ctrl_checkbox(s, "Disable Arabic text shaping",
1387 'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,
1388 I(offsetof(Config, arabicshaping)));
1389 ctrl_checkbox(s, "Disable bidirectional text display",
1390 'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,
1391 I(offsetof(Config, bidi)));
1392
1393 /*
1394 * The Window panel.
1395 */
1396 str = dupprintf("Options controlling %s's window", appname);
1397 ctrl_settitle(b, "Window", str);
1398 sfree(str);
1399
1400 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1401 ctrl_columns(s, 2, 50, 50);
1402 c = ctrl_editbox(s, "Rows", 'r', 100,
1403 HELPCTX(window_size),
1404 dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
1405 c->generic.column = 0;
1406 c = ctrl_editbox(s, "Columns", 'm', 100,
1407 HELPCTX(window_size),
1408 dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
1409 c->generic.column = 1;
1410 ctrl_columns(s, 1, 100);
1411
1412 s = ctrl_getset(b, "Window", "scrollback",
1413 "Control the scrollback in the window");
1414 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1415 HELPCTX(window_scrollback),
1416 dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
1417 ctrl_checkbox(s, "Display scrollbar", 'd',
1418 HELPCTX(window_scrollback),
1419 dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
1420 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1421 HELPCTX(window_scrollback),
1422 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
1423 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1424 HELPCTX(window_scrollback),
1425 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
1426 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1427 HELPCTX(window_erased),
1428 dlg_stdcheckbox_handler,
1429 I(offsetof(Config,erase_to_scrollback)));
1430
1431 /*
1432 * The Window/Appearance panel.
1433 */
1434 str = dupprintf("Configure the appearance of %s's window", appname);
1435 ctrl_settitle(b, "Window/Appearance", str);
1436 sfree(str);
1437
1438 s = ctrl_getset(b, "Window/Appearance", "cursor",
1439 "Adjust the use of the cursor");
1440 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1441 HELPCTX(appearance_cursor),
1442 dlg_stdradiobutton_handler,
1443 I(offsetof(Config, cursor_type)),
1444 "Block", 'l', I(0),
1445 "Underline", 'u', I(1),
1446 "Vertical line", 'v', I(2), NULL);
1447 ctrl_checkbox(s, "Cursor blinks", 'b',
1448 HELPCTX(appearance_cursor),
1449 dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
1450
1451 s = ctrl_getset(b, "Window/Appearance", "font",
1452 "Font settings");
1453 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1454 HELPCTX(appearance_font),
1455 dlg_stdfontsel_handler, I(offsetof(Config, font)));
1456
1457 s = ctrl_getset(b, "Window/Appearance", "mouse",
1458 "Adjust the use of the mouse pointer");
1459 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1460 HELPCTX(appearance_hidemouse),
1461 dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
1462
1463 s = ctrl_getset(b, "Window/Appearance", "border",
1464 "Adjust the window border");
1465 ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
1466 HELPCTX(appearance_border),
1467 dlg_stdeditbox_handler,
1468 I(offsetof(Config,window_border)), I(-1));
1469
1470 /*
1471 * The Window/Behaviour panel.
1472 */
1473 str = dupprintf("Configure the behaviour of %s's window", appname);
1474 ctrl_settitle(b, "Window/Behaviour", str);
1475 sfree(str);
1476
1477 s = ctrl_getset(b, "Window/Behaviour", "title",
1478 "Adjust the behaviour of the window title");
1479 ctrl_editbox(s, "Window title:", 't', 100,
1480 HELPCTX(appearance_title),
1481 dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
1482 I(sizeof(((Config *)0)->wintitle)));
1483 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1484 HELPCTX(appearance_title),
1485 dlg_stdcheckbox_handler,
1486 I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
1487
1488 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1489 ctrl_checkbox(s, "Warn before closing window", 'w',
1490 HELPCTX(behaviour_closewarn),
1491 dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
1492
1493 /*
1494 * The Window/Translation panel.
1495 */
1496 ctrl_settitle(b, "Window/Translation",
1497 "Options controlling character set translation");
1498
1499 s = ctrl_getset(b, "Window/Translation", "trans",
1500 "Character set translation on received data");
1501 ctrl_combobox(s, "Received data assumed to be in which character set:",
1502 'r', 100, HELPCTX(translation_codepage),
1503 codepage_handler, P(NULL), P(NULL));
1504
1505 s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
1506 ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
1507 HELPCTX(translation_cjk_ambig_wide),
1508 dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide)));
1509
1510 str = dupprintf("Adjust how %s handles line drawing characters", appname);
1511 s = ctrl_getset(b, "Window/Translation", "linedraw", str);
1512 sfree(str);
1513 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1514 HELPCTX(translation_linedraw),
1515 dlg_stdradiobutton_handler,
1516 I(offsetof(Config, vtmode)),
1517 "Use Unicode line drawing code points",'u',I(VT_UNICODE),
1518 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1519 NULL);
1520 ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
1521 HELPCTX(selection_linedraw),
1522 dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
1523
1524 /*
1525 * The Window/Selection panel.
1526 */
1527 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1528
1529 s = ctrl_getset(b, "Window/Selection", "mouse",
1530 "Control use of mouse");
1531 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1532 HELPCTX(selection_shiftdrag),
1533 dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
1534 ctrl_radiobuttons(s,
1535 "Default selection mode (Alt+drag does the other one):",
1536 NO_SHORTCUT, 2,
1537 HELPCTX(selection_rect),
1538 dlg_stdradiobutton_handler,
1539 I(offsetof(Config, rect_select)),
1540 "Normal", 'n', I(0),
1541 "Rectangular block", 'r', I(1), NULL);
1542
1543 s = ctrl_getset(b, "Window/Selection", "charclass",
1544 "Control the select-one-word-at-a-time mode");
1545 ccd = (struct charclass_data *)
1546 ctrl_alloc(b, sizeof(struct charclass_data));
1547 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1548 HELPCTX(selection_charclasses),
1549 charclass_handler, P(ccd));
1550 ccd->listbox->listbox.multisel = 1;
1551 ccd->listbox->listbox.ncols = 4;
1552 ccd->listbox->listbox.percentages = snewn(4, int);
1553 ccd->listbox->listbox.percentages[0] = 15;
1554 ccd->listbox->listbox.percentages[1] = 25;
1555 ccd->listbox->listbox.percentages[2] = 20;
1556 ccd->listbox->listbox.percentages[3] = 40;
1557 ctrl_columns(s, 2, 67, 33);
1558 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1559 HELPCTX(selection_charclasses),
1560 charclass_handler, P(ccd), P(NULL));
1561 ccd->editbox->generic.column = 0;
1562 ccd->button = ctrl_pushbutton(s, "Set", 's',
1563 HELPCTX(selection_charclasses),
1564 charclass_handler, P(ccd));
1565 ccd->button->generic.column = 1;
1566 ctrl_columns(s, 1, 100);
1567
1568 /*
1569 * The Window/Colours panel.
1570 */
1571 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1572
1573 s = ctrl_getset(b, "Window/Colours", "general",
1574 "General options for colour usage");
1575 ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
1576 HELPCTX(colours_ansi),
1577 dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));
1578 ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
1579 HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,
1580 I(offsetof(Config,xterm_256_colour)));
1581 ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1582 HELPCTX(colours_bold),
1583 dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
1584
1585 str = dupprintf("Adjust the precise colours %s displays", appname);
1586 s = ctrl_getset(b, "Window/Colours", "adjust", str);
1587 sfree(str);
1588 ctrl_text(s, "Select a colour from the list, and then click the"
1589 " Modify button to change its appearance.",
1590 HELPCTX(colours_config));
1591 ctrl_columns(s, 2, 67, 33);
1592 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1593 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1594 HELPCTX(colours_config), colour_handler, P(cd));
1595 cd->listbox->generic.column = 0;
1596 cd->listbox->listbox.height = 7;
1597 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1598 c->generic.column = 1;
1599 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1600 colour_handler, P(cd), P(NULL));
1601 cd->redit->generic.column = 1;
1602 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1603 colour_handler, P(cd), P(NULL));
1604 cd->gedit->generic.column = 1;
1605 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1606 colour_handler, P(cd), P(NULL));
1607 cd->bedit->generic.column = 1;
1608 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1609 colour_handler, P(cd));
1610 cd->button->generic.column = 1;
1611 ctrl_columns(s, 1, 100);
1612
1613 /*
1614 * The Connection panel. This doesn't show up if we're in a
1615 * non-network utility such as pterm. We tell this by being
1616 * passed a protocol < 0.
1617 */
1618 if (protocol >= 0) {
1619 ctrl_settitle(b, "Connection", "Options controlling the connection");
1620
1621 s = ctrl_getset(b, "Connection", "keepalive",
1622 "Sending of null packets to keep session active");
1623 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1624 HELPCTX(connection_keepalive),
1625 dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
1626 I(-1));
1627
1628 if (!midsession) {
1629 s = ctrl_getset(b, "Connection", "tcp",
1630 "Low-level TCP connection options");
1631 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
1632 'n', HELPCTX(connection_nodelay),
1633 dlg_stdcheckbox_handler,
1634 I(offsetof(Config,tcp_nodelay)));
1635 ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
1636 'p', HELPCTX(connection_tcpkeepalive),
1637 dlg_stdcheckbox_handler,
1638 I(offsetof(Config,tcp_keepalives)));
1639 #ifndef NO_IPV6
1640 s = ctrl_getset(b, "Connection", "ipversion",
1641 "Internet protocol version");
1642 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1643 HELPCTX(connection_ipversion),
1644 dlg_stdradiobutton_handler,
1645 I(offsetof(Config, addressfamily)),
1646 "Auto", 'u', I(ADDRTYPE_UNSPEC),
1647 "IPv4", '4', I(ADDRTYPE_IPV4),
1648 "IPv6", '6', I(ADDRTYPE_IPV6),
1649 NULL);
1650 #endif
1651 }
1652
1653 /*
1654 * A sub-panel Connection/Data, containing options that
1655 * decide on data to send to the server.
1656 */
1657 if (!midsession) {
1658 ctrl_settitle(b, "Connection/Data", "Data to send to the server");
1659
1660 s = ctrl_getset(b, "Connection/Data", "login",
1661 "Login details");
1662 ctrl_editbox(s, "Auto-login username", 'u', 50,
1663 HELPCTX(connection_username),
1664 dlg_stdeditbox_handler, I(offsetof(Config,username)),
1665 I(sizeof(((Config *)0)->username)));
1666
1667 s = ctrl_getset(b, "Connection/Data", "term",
1668 "Terminal details");
1669 ctrl_editbox(s, "Terminal-type string", 't', 50,
1670 HELPCTX(connection_termtype),
1671 dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
1672 I(sizeof(((Config *)0)->termtype)));
1673 ctrl_editbox(s, "Terminal speeds", 's', 50,
1674 HELPCTX(connection_termspeed),
1675 dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1676 I(sizeof(((Config *)0)->termspeed)));
1677
1678 s = ctrl_getset(b, "Connection/Data", "env",
1679 "Environment variables");
1680 ctrl_columns(s, 2, 80, 20);
1681 ed = (struct environ_data *)
1682 ctrl_alloc(b, sizeof(struct environ_data));
1683 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1684 HELPCTX(telnet_environ),
1685 environ_handler, P(ed), P(NULL));
1686 ed->varbox->generic.column = 0;
1687 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1688 HELPCTX(telnet_environ),
1689 environ_handler, P(ed), P(NULL));
1690 ed->valbox->generic.column = 0;
1691 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1692 HELPCTX(telnet_environ),
1693 environ_handler, P(ed));
1694 ed->addbutton->generic.column = 1;
1695 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1696 HELPCTX(telnet_environ),
1697 environ_handler, P(ed));
1698 ed->rembutton->generic.column = 1;
1699 ctrl_columns(s, 1, 100);
1700 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1701 HELPCTX(telnet_environ),
1702 environ_handler, P(ed));
1703 ed->listbox->listbox.height = 3;
1704 ed->listbox->listbox.ncols = 2;
1705 ed->listbox->listbox.percentages = snewn(2, int);
1706 ed->listbox->listbox.percentages[0] = 30;
1707 ed->listbox->listbox.percentages[1] = 70;
1708 }
1709
1710 }
1711
1712 if (!midsession) {
1713 /*
1714 * The Connection/Proxy panel.
1715 */
1716 ctrl_settitle(b, "Connection/Proxy",
1717 "Options controlling proxy usage");
1718
1719 s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
1720 ctrl_radiobuttons(s, "Proxy type:", 't', 3,
1721 HELPCTX(proxy_type),
1722 dlg_stdradiobutton_handler,
1723 I(offsetof(Config, proxy_type)),
1724 "None", I(PROXY_NONE),
1725 "SOCKS 4", I(PROXY_SOCKS4),
1726 "SOCKS 5", I(PROXY_SOCKS5),
1727 "HTTP", I(PROXY_HTTP),
1728 "Telnet", I(PROXY_TELNET),
1729 NULL);
1730 ctrl_columns(s, 2, 80, 20);
1731 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1732 HELPCTX(proxy_main),
1733 dlg_stdeditbox_handler,
1734 I(offsetof(Config,proxy_host)),
1735 I(sizeof(((Config *)0)->proxy_host)));
1736 c->generic.column = 0;
1737 c = ctrl_editbox(s, "Port", 'p', 100,
1738 HELPCTX(proxy_main),
1739 dlg_stdeditbox_handler,
1740 I(offsetof(Config,proxy_port)),
1741 I(-1));
1742 c->generic.column = 1;
1743 ctrl_columns(s, 1, 100);
1744 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1745 HELPCTX(proxy_exclude),
1746 dlg_stdeditbox_handler,
1747 I(offsetof(Config,proxy_exclude_list)),
1748 I(sizeof(((Config *)0)->proxy_exclude_list)));
1749 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1750 HELPCTX(proxy_exclude),
1751 dlg_stdcheckbox_handler,
1752 I(offsetof(Config,even_proxy_localhost)));
1753 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1754 HELPCTX(proxy_dns),
1755 dlg_stdradiobutton_handler,
1756 I(offsetof(Config, proxy_dns)),
1757 "No", I(FORCE_OFF),
1758 "Auto", I(AUTO),
1759 "Yes", I(FORCE_ON), NULL);
1760 ctrl_editbox(s, "Username", 'u', 60,
1761 HELPCTX(proxy_auth),
1762 dlg_stdeditbox_handler,
1763 I(offsetof(Config,proxy_username)),
1764 I(sizeof(((Config *)0)->proxy_username)));
1765 c = ctrl_editbox(s, "Password", 'w', 60,
1766 HELPCTX(proxy_auth),
1767 dlg_stdeditbox_handler,
1768 I(offsetof(Config,proxy_password)),
1769 I(sizeof(((Config *)0)->proxy_password)));
1770 c->editbox.password = 1;
1771 ctrl_editbox(s, "Telnet command", 'm', 100,
1772 HELPCTX(proxy_command),
1773 dlg_stdeditbox_handler,
1774 I(offsetof(Config,proxy_telnet_command)),
1775 I(sizeof(((Config *)0)->proxy_telnet_command)));
1776 }
1777
1778 /*
1779 * The Telnet panel exists in the base config box, and in a
1780 * mid-session reconfig box _if_ we're using Telnet.
1781 */
1782 if (!midsession || protocol == PROT_TELNET) {
1783 /*
1784 * The Connection/Telnet panel.
1785 */
1786 ctrl_settitle(b, "Connection/Telnet",
1787 "Options controlling Telnet connections");
1788
1789 s = ctrl_getset(b, "Connection/Telnet", "protocol",
1790 "Telnet protocol adjustments");
1791
1792 if (!midsession) {
1793 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
1794 NO_SHORTCUT, 2,
1795 HELPCTX(telnet_oldenviron),
1796 dlg_stdradiobutton_handler,
1797 I(offsetof(Config, rfc_environ)),
1798 "BSD (commonplace)", 'b', I(0),
1799 "RFC 1408 (unusual)", 'f', I(1), NULL);
1800 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
1801 HELPCTX(telnet_passive),
1802 dlg_stdradiobutton_handler,
1803 I(offsetof(Config, passive_telnet)),
1804 "Passive", I(1), "Active", I(0), NULL);
1805 }
1806 ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
1807 HELPCTX(telnet_specialkeys),
1808 dlg_stdcheckbox_handler,
1809 I(offsetof(Config,telnet_keyboard)));
1810 ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
1811 'm', HELPCTX(telnet_newline),
1812 dlg_stdcheckbox_handler,
1813 I(offsetof(Config,telnet_newline)));
1814 }
1815
1816 if (!midsession) {
1817
1818 /*
1819 * The Connection/Rlogin panel.
1820 */
1821 ctrl_settitle(b, "Connection/Rlogin",
1822 "Options controlling Rlogin connections");
1823
1824 s = ctrl_getset(b, "Connection/Rlogin", "data",
1825 "Data to send to the server");
1826 ctrl_editbox(s, "Local username:", 'l', 50,
1827 HELPCTX(rlogin_localuser),
1828 dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
1829 I(sizeof(((Config *)0)->localusername)));
1830
1831 }
1832
1833 /*
1834 * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
1835 * when we're not doing SSH.
1836 */
1837
1838 if (have_backend(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {
1839
1840 /*
1841 * The Connection/SSH panel.
1842 */
1843 ctrl_settitle(b, "Connection/SSH",
1844 "Options controlling SSH connections");
1845
1846 if (midsession && protcfginfo == 1) {
1847 s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
1848 ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
1849 "session; it is only here so that sub-panels of it can "
1850 "exist without looking strange.", HELPCTX(no_help));
1851 }
1852
1853 if (!midsession) {
1854
1855 s = ctrl_getset(b, "Connection/SSH", "data",
1856 "Data to send to the server");
1857 ctrl_editbox(s, "Remote command:", 'r', 100,
1858 HELPCTX(ssh_command),
1859 dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
1860 I(sizeof(((Config *)0)->remote_cmd)));
1861
1862 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1863 ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
1864 HELPCTX(ssh_noshell),
1865 dlg_stdcheckbox_handler,
1866 I(offsetof(Config,ssh_no_shell)));
1867 }
1868
1869 if (!midsession || protcfginfo != 1) {
1870 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1871
1872 ctrl_checkbox(s, "Enable compression", 'e',
1873 HELPCTX(ssh_compress),
1874 dlg_stdcheckbox_handler,
1875 I(offsetof(Config,compression)));
1876 }
1877
1878 if (!midsession) {
1879 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1880
1881 ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
1882 HELPCTX(ssh_protocol),
1883 dlg_stdradiobutton_handler,
1884 I(offsetof(Config, sshprot)),
1885 "1 only", 'l', I(0),
1886 "1", '1', I(1),
1887 "2", '2', I(2),
1888 "2 only", 'y', I(3), NULL);
1889 }
1890
1891 if (!midsession || protcfginfo != 1) {
1892 s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
1893 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
1894 HELPCTX(ssh_ciphers),
1895 cipherlist_handler, P(NULL));
1896 c->listbox.height = 6;
1897
1898 ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
1899 HELPCTX(ssh_ciphers),
1900 dlg_stdcheckbox_handler,
1901 I(offsetof(Config,ssh2_des_cbc)));
1902 }
1903
1904 /*
1905 * The Connection/SSH/Kex panel. (Owing to repeat key
1906 * exchange, this is all meaningful in mid-session _if_
1907 * we're using SSH-2 or haven't decided yet.)
1908 */
1909 if (protcfginfo != 1) {
1910 ctrl_settitle(b, "Connection/SSH/Kex",
1911 "Options controlling SSH key exchange");
1912
1913 s = ctrl_getset(b, "Connection/SSH/Kex", "main",
1914 "Key exchange algorithm options");
1915 c = ctrl_draglist(s, "Algorithm selection policy:", 's',
1916 HELPCTX(ssh_kexlist),
1917 kexlist_handler, P(NULL));
1918 c->listbox.height = 5;
1919
1920 s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
1921 "Options controlling key re-exchange");
1922
1923 ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
1924 HELPCTX(ssh_kex_repeat),
1925 dlg_stdeditbox_handler,
1926 I(offsetof(Config,ssh_rekey_time)),
1927 I(-1));
1928 ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
1929 HELPCTX(ssh_kex_repeat),
1930 dlg_stdeditbox_handler,
1931 I(offsetof(Config,ssh_rekey_data)),
1932 I(16));
1933 ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
1934 HELPCTX(ssh_kex_repeat));
1935 }
1936
1937 if (!midsession) {
1938
1939 /*
1940 * The Connection/SSH/Auth panel.
1941 */
1942 ctrl_settitle(b, "Connection/SSH/Auth",
1943 "Options controlling SSH authentication");
1944
1945 s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
1946 ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
1947 HELPCTX(ssh_auth_bypass),
1948 dlg_stdcheckbox_handler,
1949 I(offsetof(Config,ssh_no_userauth)));
1950
1951 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
1952 "Authentication methods");
1953 ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
1954 HELPCTX(ssh_auth_pageant),
1955 dlg_stdcheckbox_handler,
1956 I(offsetof(Config,tryagent)));
1957 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
1958 HELPCTX(ssh_auth_tis),
1959 dlg_stdcheckbox_handler,
1960 I(offsetof(Config,try_tis_auth)));
1961 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
1962 'i', HELPCTX(ssh_auth_ki),
1963 dlg_stdcheckbox_handler,
1964 I(offsetof(Config,try_ki_auth)));
1965
1966 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
1967 "Authentication parameters");
1968 ctrl_checkbox(s, "Allow agent forwarding", 'f',
1969 HELPCTX(ssh_auth_agentfwd),
1970 dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
1971 ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", 'u',
1972 HELPCTX(ssh_auth_changeuser),
1973 dlg_stdcheckbox_handler,
1974 I(offsetof(Config,change_username)));
1975 ctrl_filesel(s, "Private key file for authentication:", 'k',
1976 FILTER_KEY_FILES, FALSE, "Select private key file",
1977 HELPCTX(ssh_auth_privkey),
1978 dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
1979 }
1980
1981 if (!midsession) {
1982 /*
1983 * The Connection/SSH/TTY panel.
1984 */
1985 ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
1986
1987 s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
1988 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
1989 HELPCTX(ssh_nopty),
1990 dlg_stdcheckbox_handler,
1991 I(offsetof(Config,nopty)));
1992
1993 s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
1994 "Terminal modes");
1995 td = (struct ttymodes_data *)
1996 ctrl_alloc(b, sizeof(struct ttymodes_data));
1997 ctrl_columns(s, 2, 75, 25);
1998 c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
1999 c->generic.column = 0;
2000 td->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2001 HELPCTX(ssh_ttymodes),
2002 ttymodes_handler, P(td));
2003 td->rembutton->generic.column = 1;
2004 td->rembutton->generic.tabdelay = 1;
2005 ctrl_columns(s, 1, 100);
2006 td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2007 HELPCTX(ssh_ttymodes),
2008 ttymodes_handler, P(td));
2009 td->listbox->listbox.multisel = 1;
2010 td->listbox->listbox.height = 4;
2011 td->listbox->listbox.ncols = 2;
2012 td->listbox->listbox.percentages = snewn(2, int);
2013 td->listbox->listbox.percentages[0] = 40;
2014 td->listbox->listbox.percentages[1] = 60;
2015 ctrl_tabdelay(s, td->rembutton);
2016 ctrl_columns(s, 2, 75, 25);
2017 td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,
2018 HELPCTX(ssh_ttymodes),
2019 ttymodes_handler, P(td));
2020 td->modelist->generic.column = 0;
2021 td->addbutton = ctrl_pushbutton(s, "Add", 'd',
2022 HELPCTX(ssh_ttymodes),
2023 ttymodes_handler, P(td));
2024 td->addbutton->generic.column = 1;
2025 td->addbutton->generic.tabdelay = 1;
2026 ctrl_columns(s, 1, 100); /* column break */
2027 /* Bit of a hack to get the value radio buttons and
2028 * edit-box on the same row. */
2029 ctrl_columns(s, 3, 25, 50, 25);
2030 c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));
2031 c->generic.column = 0;
2032 td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,
2033 HELPCTX(ssh_ttymodes),
2034 ttymodes_handler, P(td),
2035 "Auto", NO_SHORTCUT, P(NULL),
2036 "This:", NO_SHORTCUT, P(NULL),
2037 NULL);
2038 td->valradio->generic.column = 1;
2039 td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
2040 HELPCTX(ssh_ttymodes),
2041 ttymodes_handler, P(td), P(NULL));
2042 td->valbox->generic.column = 2;
2043 ctrl_tabdelay(s, td->addbutton);
2044
2045 }
2046
2047 if (!midsession) {
2048 /*
2049 * The Connection/SSH/X11 panel.
2050 */
2051 ctrl_settitle(b, "Connection/SSH/X11",
2052 "Options controlling SSH X11 forwarding");
2053
2054 s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
2055 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
2056 HELPCTX(ssh_tunnels_x11),
2057 dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
2058 ctrl_editbox(s, "X display location", 'x', 50,
2059 HELPCTX(ssh_tunnels_x11),
2060 dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
2061 I(sizeof(((Config *)0)->x11_display)));
2062 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
2063 HELPCTX(ssh_tunnels_x11auth),
2064 dlg_stdradiobutton_handler,
2065 I(offsetof(Config, x11_auth)),
2066 "MIT-Magic-Cookie-1", I(X11_MIT),
2067 "XDM-Authorization-1", I(X11_XDM), NULL);
2068 }
2069
2070 /*
2071 * The Tunnels panel _is_ still available in mid-session.
2072 */
2073 ctrl_settitle(b, "Connection/SSH/Tunnels",
2074 "Options controlling SSH port forwarding");
2075
2076 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
2077 "Port forwarding");
2078 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
2079 HELPCTX(ssh_tunnels_portfwd_localhost),
2080 dlg_stdcheckbox_handler,
2081 I(offsetof(Config,lport_acceptall)));
2082 ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
2083 HELPCTX(ssh_tunnels_portfwd_localhost),
2084 dlg_stdcheckbox_handler,
2085 I(offsetof(Config,rport_acceptall)));
2086
2087 ctrl_columns(s, 3, 55, 20, 25);
2088 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
2089 c->generic.column = COLUMN_FIELD(0,2);
2090 /* You want to select from the list, _then_ hit Remove. So tab order
2091 * should be that way round. */
2092 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
2093 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
2094 HELPCTX(ssh_tunnels_portfwd),
2095 portfwd_handler, P(pfd));
2096 pfd->rembutton->generic.column = 2;
2097 pfd->rembutton->generic.tabdelay = 1;
2098 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
2099 HELPCTX(ssh_tunnels_portfwd),
2100 portfwd_handler, P(pfd));
2101 pfd->listbox->listbox.height = 3;
2102 pfd->listbox->listbox.ncols = 2;
2103 pfd->listbox->listbox.percentages = snewn(2, int);
2104 pfd->listbox->listbox.percentages[0] = 20;
2105 pfd->listbox->listbox.percentages[1] = 80;
2106 ctrl_tabdelay(s, pfd->rembutton);
2107 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
2108 /* You want to enter source, destination and type, _then_ hit Add.
2109 * Again, we adjust the tab order to reflect this. */
2110 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
2111 HELPCTX(ssh_tunnels_portfwd),
2112 portfwd_handler, P(pfd));
2113 pfd->addbutton->generic.column = 2;
2114 pfd->addbutton->generic.tabdelay = 1;
2115 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
2116 HELPCTX(ssh_tunnels_portfwd),
2117 portfwd_handler, P(pfd), P(NULL));
2118 pfd->sourcebox->generic.column = 0;
2119 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
2120 HELPCTX(ssh_tunnels_portfwd),
2121 portfwd_handler, P(pfd), P(NULL));
2122 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2123 HELPCTX(ssh_tunnels_portfwd),
2124 portfwd_handler, P(pfd),
2125 "Local", 'l', P(NULL),
2126 "Remote", 'm', P(NULL),
2127 "Dynamic", 'y', P(NULL),
2128 NULL);
2129 #ifndef NO_IPV6
2130 pfd->addressfamily =
2131 ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
2132 HELPCTX(ssh_tunnels_portfwd_ipversion),
2133 portfwd_handler, P(pfd),
2134 "Auto", 'u', I(ADDRTYPE_UNSPEC),
2135 "IPv4", '4', I(ADDRTYPE_IPV4),
2136 "IPv6", '6', I(ADDRTYPE_IPV6),
2137 NULL);
2138 #endif
2139 ctrl_tabdelay(s, pfd->addbutton);
2140 ctrl_columns(s, 1, 100);
2141
2142 if (!midsession) {
2143 /*
2144 * The Connection/SSH/Bugs panel.
2145 */
2146 ctrl_settitle(b, "Connection/SSH/Bugs",
2147 "Workarounds for SSH server bugs");
2148
2149 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
2150 "Detection of known bugs in SSH servers");
2151 ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
2152 HELPCTX(ssh_bugs_ignore1),
2153 sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
2154 ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
2155 HELPCTX(ssh_bugs_plainpw1),
2156 sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
2157 ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
2158 HELPCTX(ssh_bugs_rsa1),
2159 sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
2160 ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
2161 HELPCTX(ssh_bugs_hmac2),
2162 sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
2163 ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
2164 HELPCTX(ssh_bugs_derivekey2),
2165 sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
2166 ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
2167 HELPCTX(ssh_bugs_rsapad2),
2168 sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
2169 ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
2170 HELPCTX(ssh_bugs_pksessid2),
2171 sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
2172 ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
2173 HELPCTX(ssh_bugs_rekey2),
2174 sshbug_handler, I(offsetof(Config,sshbug_rekey2)));
2175 }
2176 }
2177 }