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