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