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