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