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