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