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