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