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