Unix can't sensibly enumerate printers (since they're defined as
[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 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
730 ssd = (struct sessionsaver_data *)
731 ctrl_alloc(b, sizeof(struct sessionsaver_data));
732 ssd->sesslist = (midsession ? NULL : sesslist);
733
734 /*
735 * The standard panel that appears at the bottom of all panels:
736 * Open, Cancel, Apply etc.
737 */
738 s = ctrl_getset(b, "", "", "");
739 ctrl_columns(s, 5, 20, 20, 20, 20, 20);
740 ssd->okbutton = ctrl_pushbutton(s,
741 (midsession ? "Apply" : "Open"),
742 (char)(midsession ? 'a' : 'o'),
743 HELPCTX(no_help),
744 sessionsaver_handler, P(ssd));
745 ssd->okbutton->button.isdefault = TRUE;
746 ssd->okbutton->generic.column = 3;
747 ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
748 sessionsaver_handler, P(ssd));
749 ssd->cancelbutton->button.iscancel = TRUE;
750 ssd->cancelbutton->generic.column = 4;
751 /* We carefully don't close the 5-column part, so that platform-
752 * specific add-ons can put extra buttons alongside Open and Cancel. */
753
754 /*
755 * The Session panel.
756 */
757 ctrl_settitle(b, "Session", "Basic options for your PuTTY session");
758
759 if (!midsession) {
760 s = ctrl_getset(b, "Session", "hostport",
761 "Specify your connection by host name or IP address");
762 ctrl_columns(s, 2, 75, 25);
763 c = ctrl_editbox(s, "Host Name (or IP address)", 'n', 100,
764 HELPCTX(session_hostname),
765 dlg_stdeditbox_handler, I(offsetof(Config,host)),
766 I(sizeof(((Config *)0)->host)));
767 c->generic.column = 0;
768 c = ctrl_editbox(s, "Port", 'p', 100, HELPCTX(session_hostname),
769 dlg_stdeditbox_handler,
770 I(offsetof(Config,port)), I(-1));
771 c->generic.column = 1;
772 ctrl_columns(s, 1, 100);
773 if (backends[3].name == NULL) {
774 ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 3,
775 HELPCTX(session_hostname),
776 protocolbuttons_handler, P(c),
777 "Raw", 'r', I(PROT_RAW),
778 "Telnet", 't', I(PROT_TELNET),
779 "Rlogin", 'i', I(PROT_RLOGIN),
780 NULL);
781 } else {
782 ctrl_radiobuttons(s, "Protocol:", NO_SHORTCUT, 4,
783 HELPCTX(session_hostname),
784 protocolbuttons_handler, P(c),
785 "Raw", 'r', I(PROT_RAW),
786 "Telnet", 't', I(PROT_TELNET),
787 "Rlogin", 'i', I(PROT_RLOGIN),
788 "SSH", 's', I(PROT_SSH),
789 NULL);
790 }
791
792 s = ctrl_getset(b, "Session", "savedsessions",
793 "Load, save or delete a stored session");
794 ctrl_columns(s, 2, 75, 25);
795 ssd->sesslist = sesslist;
796 ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
797 HELPCTX(session_saved),
798 sessionsaver_handler, P(ssd), P(NULL));
799 ssd->editbox->generic.column = 0;
800 /* Reset columns so that the buttons are alongside the list, rather
801 * than alongside that edit box. */
802 ctrl_columns(s, 1, 100);
803 ctrl_columns(s, 2, 75, 25);
804 ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
805 HELPCTX(session_saved),
806 sessionsaver_handler, P(ssd));
807 ssd->listbox->generic.column = 0;
808 ssd->listbox->listbox.height = 7;
809 ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
810 HELPCTX(session_saved),
811 sessionsaver_handler, P(ssd));
812 ssd->loadbutton->generic.column = 1;
813 ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
814 HELPCTX(session_saved),
815 sessionsaver_handler, P(ssd));
816 ssd->savebutton->generic.column = 1;
817 ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
818 HELPCTX(session_saved),
819 sessionsaver_handler, P(ssd));
820 ssd->delbutton->generic.column = 1;
821 ctrl_columns(s, 1, 100);
822 }
823
824 s = ctrl_getset(b, "Session", "otheropts", NULL);
825 c = ctrl_radiobuttons(s, "Close window on exit:", 'w', 4,
826 HELPCTX(session_coe),
827 dlg_stdradiobutton_handler,
828 I(offsetof(Config, close_on_exit)),
829 "Always", I(FORCE_ON),
830 "Never", I(FORCE_OFF),
831 "Only on clean exit", I(AUTO), NULL);
832
833 /*
834 * The Session/Logging panel.
835 */
836 ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
837
838 s = ctrl_getset(b, "Session/Logging", "main", NULL);
839 ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 1,
840 HELPCTX(logging_main),
841 dlg_stdradiobutton_handler, I(offsetof(Config, logtype)),
842 "Logging turned off completely", 't', I(LGTYP_NONE),
843 "Log printable output only", 'p', I(LGTYP_ASCII),
844 "Log all session output", 'l', I(LGTYP_DEBUG),
845 "Log SSH packet data", 's', I(LGTYP_PACKETS),
846 NULL);
847 ctrl_filesel(s, "Log file name:", 'f',
848 NULL, TRUE, "Select session log file name",
849 HELPCTX(logging_filename),
850 dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));
851 ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
852 " &T for time, and &H for host name)",
853 HELPCTX(logging_filename));
854 ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
855 HELPCTX(logging_exists),
856 dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),
857 "Always overwrite it", I(LGXF_OVR),
858 "Always append to the end of it", I(LGXF_APN),
859 "Ask the user every time", I(LGXF_ASK), NULL);
860
861 /*
862 * The Terminal panel.
863 */
864 ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
865
866 s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
867 ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
868 HELPCTX(terminal_autowrap),
869 dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));
870 ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
871 HELPCTX(terminal_decom),
872 dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));
873 ctrl_checkbox(s, "Implicit CR in every LF", 'r',
874 HELPCTX(terminal_lfhascr),
875 dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));
876 ctrl_checkbox(s, "Use background colour to erase screen", 'e',
877 HELPCTX(terminal_bce),
878 dlg_stdcheckbox_handler, I(offsetof(Config,bce)));
879 ctrl_checkbox(s, "Enable blinking text", 'n',
880 HELPCTX(terminal_blink),
881 dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));
882 ctrl_editbox(s, "Answerback to ^E:", 's', 100,
883 HELPCTX(terminal_answerback),
884 dlg_stdeditbox_handler, I(offsetof(Config,answerback)),
885 I(sizeof(((Config *)0)->answerback)));
886
887 s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
888 ctrl_radiobuttons(s, "Local echo:", 'l', 3,
889 HELPCTX(terminal_localecho),
890 dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),
891 "Auto", I(AUTO),
892 "Force on", I(FORCE_ON),
893 "Force off", I(FORCE_OFF), NULL);
894 ctrl_radiobuttons(s, "Local line editing:", 't', 3,
895 HELPCTX(terminal_localedit),
896 dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),
897 "Auto", I(AUTO),
898 "Force on", I(FORCE_ON),
899 "Force off", I(FORCE_OFF), NULL);
900
901 s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
902 ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
903 HELPCTX(terminal_printing),
904 printerbox_handler, P(NULL), P(NULL));
905
906 /*
907 * The Terminal/Keyboard panel.
908 */
909 ctrl_settitle(b, "Terminal/Keyboard",
910 "Options controlling the effects of keys");
911
912 s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
913 "Change the sequences sent by:");
914 ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
915 HELPCTX(keyboard_backspace),
916 dlg_stdradiobutton_handler,
917 I(offsetof(Config, bksp_is_delete)),
918 "Control-H", I(0), "Control-? (127)", I(1), NULL);
919 ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
920 HELPCTX(keyboard_homeend),
921 dlg_stdradiobutton_handler,
922 I(offsetof(Config, rxvt_homeend)),
923 "Standard", I(0), "rxvt", I(1), NULL);
924 ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
925 HELPCTX(keyboard_funkeys),
926 dlg_stdradiobutton_handler,
927 I(offsetof(Config, funky_type)),
928 "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
929 "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
930
931 s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
932 "Application keypad settings:");
933 ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
934 HELPCTX(keyboard_appcursor),
935 dlg_stdradiobutton_handler,
936 I(offsetof(Config, app_cursor)),
937 "Normal", I(0), "Application", I(1), NULL);
938 ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
939 HELPCTX(keyboard_appkeypad),
940 numeric_keypad_handler, P(NULL),
941 "Normal", I(0), "Application", I(1), "NetHack", I(2),
942 NULL);
943
944 /*
945 * The Terminal/Bell panel.
946 */
947 ctrl_settitle(b, "Terminal/Bell",
948 "Options controlling the terminal bell");
949
950 s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
951 ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
952 HELPCTX(bell_style),
953 dlg_stdradiobutton_handler, I(offsetof(Config, beep)),
954 "None (bell disabled)", I(BELL_DISABLED),
955 "Make default system alert sound", I(BELL_DEFAULT),
956 "Visual bell (flash window)", I(BELL_VISUAL), NULL);
957
958 s = ctrl_getset(b, "Terminal/Bell", "overload",
959 "Control the bell overload behaviour");
960 ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
961 HELPCTX(bell_overload),
962 dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));
963 ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
964 HELPCTX(bell_overload),
965 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));
966 ctrl_editbox(s, "... in this many seconds", 't', 20,
967 HELPCTX(bell_overload),
968 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),
969 I(-TICKSPERSEC));
970 ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
971 HELPCTX(bell_overload));
972 ctrl_editbox(s, "Seconds of silence required", 's', 20,
973 HELPCTX(bell_overload),
974 dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),
975 I(-TICKSPERSEC));
976
977 /*
978 * The Terminal/Features panel.
979 */
980 ctrl_settitle(b, "Terminal/Features",
981 "Enabling and disabling advanced terminal features");
982
983 s = ctrl_getset(b, "Terminal/Features", "main", NULL);
984 ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
985 HELPCTX(features_application),
986 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));
987 ctrl_checkbox(s, "Disable application keypad mode", 'k',
988 HELPCTX(features_application),
989 dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));
990 ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
991 HELPCTX(features_mouse),
992 dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));
993 ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
994 HELPCTX(features_resize),
995 dlg_stdcheckbox_handler,
996 I(offsetof(Config,no_remote_resize)));
997 ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
998 HELPCTX(features_altscreen),
999 dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));
1000 ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
1001 HELPCTX(features_retitle),
1002 dlg_stdcheckbox_handler,
1003 I(offsetof(Config,no_remote_wintitle)));
1004 ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
1005 HELPCTX(features_dbackspace),
1006 dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));
1007 ctrl_checkbox(s, "Disable remote-controlled character set configuration",
1008 'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
1009 I(offsetof(Config,no_remote_charset)));
1010
1011 /*
1012 * The Window panel.
1013 */
1014 ctrl_settitle(b, "Window", "Options controlling PuTTY's window");
1015
1016 s = ctrl_getset(b, "Window", "size", "Set the size of the window");
1017 ctrl_columns(s, 2, 50, 50);
1018 c = ctrl_editbox(s, "Rows", 'r', 100,
1019 HELPCTX(window_size),
1020 dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));
1021 c->generic.column = 0;
1022 c = ctrl_editbox(s, "Columns", 'm', 100,
1023 HELPCTX(window_size),
1024 dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));
1025 c->generic.column = 1;
1026 ctrl_columns(s, 1, 100);
1027
1028 s = ctrl_getset(b, "Window", "scrollback",
1029 "Control the scrollback in the window");
1030 ctrl_editbox(s, "Lines of scrollback", 's', 50,
1031 HELPCTX(window_scrollback),
1032 dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));
1033 ctrl_checkbox(s, "Display scrollbar", 'd',
1034 HELPCTX(window_scrollback),
1035 dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));
1036 ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
1037 HELPCTX(window_scrollback),
1038 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
1039 ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
1040 HELPCTX(window_scrollback),
1041 dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
1042 ctrl_checkbox(s, "Push erased text into scrollback", 'e',
1043 HELPCTX(window_erased),
1044 dlg_stdcheckbox_handler,
1045 I(offsetof(Config,erase_to_scrollback)));
1046
1047 /*
1048 * The Window/Appearance panel.
1049 */
1050 ctrl_settitle(b, "Window/Appearance",
1051 "Configure the appearance of PuTTY's window");
1052
1053 s = ctrl_getset(b, "Window/Appearance", "cursor",
1054 "Adjust the use of the cursor");
1055 ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1056 HELPCTX(appearance_cursor),
1057 dlg_stdradiobutton_handler,
1058 I(offsetof(Config, cursor_type)),
1059 "Block", 'l', I(0),
1060 "Underline", 'u', I(1),
1061 "Vertical line", 'v', I(2), NULL);
1062 ctrl_checkbox(s, "Cursor blinks", 'b',
1063 HELPCTX(appearance_cursor),
1064 dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
1065
1066 s = ctrl_getset(b, "Window/Appearance", "font",
1067 "Font settings");
1068 ctrl_fontsel(s, "Font used in the terminal window", 'n',
1069 HELPCTX(appearance_font),
1070 dlg_stdfontsel_handler, I(offsetof(Config, font)));
1071
1072 s = ctrl_getset(b, "Window/Appearance", "mouse",
1073 "Adjust the use of the mouse pointer");
1074 ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1075 HELPCTX(appearance_hidemouse),
1076 dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
1077
1078 s = ctrl_getset(b, "Window/Appearance", "border",
1079 "Adjust the window border");
1080 ctrl_editbox(s, "Gap between text and window edge:", NO_SHORTCUT, 20,
1081 HELPCTX(appearance_border),
1082 dlg_stdeditbox_handler,
1083 I(offsetof(Config,window_border)), I(-1));
1084
1085 /*
1086 * The Window/Behaviour panel.
1087 */
1088 ctrl_settitle(b, "Window/Behaviour",
1089 "Configure the behaviour of PuTTY's window");
1090
1091 s = ctrl_getset(b, "Window/Behaviour", "title",
1092 "Adjust the behaviour of the window title");
1093 ctrl_editbox(s, "Window title:", 't', 100,
1094 HELPCTX(appearance_title),
1095 dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
1096 I(sizeof(((Config *)0)->wintitle)));
1097 ctrl_checkbox(s, "Separate window and icon titles", 'i',
1098 HELPCTX(appearance_title),
1099 dlg_stdcheckbox_handler,
1100 I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
1101
1102 s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1103 ctrl_checkbox(s, "Warn before closing window", 'w',
1104 HELPCTX(behaviour_closewarn),
1105 dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
1106
1107 /*
1108 * The Window/Translation panel.
1109 */
1110 ctrl_settitle(b, "Window/Translation",
1111 "Options controlling character set translation");
1112
1113 s = ctrl_getset(b, "Window/Translation", "trans",
1114 "Character set translation on received data");
1115 ctrl_combobox(s, "Received data assumed to be in which character set:",
1116 'r', 100, HELPCTX(translation_codepage),
1117 codepage_handler, P(NULL), P(NULL));
1118
1119 s = ctrl_getset(b, "Window/Translation", "linedraw",
1120 "Adjust how PuTTY displays line drawing characters");
1121 ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1122 HELPCTX(translation_linedraw),
1123 dlg_stdradiobutton_handler,
1124 I(offsetof(Config, vtmode)),
1125 "Font has XWindows encoding", 'x', I(VT_XWINDOWS),
1126 "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1127 "Unicode mode", 'u', I(VT_UNICODE), NULL);
1128
1129 /*
1130 * The Window/Selection panel.
1131 */
1132 ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1133
1134 s = ctrl_getset(b, "Window/Selection", "trans",
1135 "Translation of pasted characters");
1136 ctrl_checkbox(s, "Don't translate line drawing chars into +, - and |",'d',
1137 HELPCTX(selection_linedraw),
1138 dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
1139
1140 s = ctrl_getset(b, "Window/Selection", "mouse",
1141 "Control use of mouse");
1142 ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1143 HELPCTX(selection_shiftdrag),
1144 dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
1145 ctrl_radiobuttons(s,
1146 "Default selection mode (Alt+drag does the other one):",
1147 NO_SHORTCUT, 2,
1148 HELPCTX(selection_rect),
1149 dlg_stdradiobutton_handler,
1150 I(offsetof(Config, rect_select)),
1151 "Normal", 'n', I(0),
1152 "Rectangular block", 'r', I(1), NULL);
1153
1154 s = ctrl_getset(b, "Window/Selection", "charclass",
1155 "Control the select-one-word-at-a-time mode");
1156 ccd = (struct charclass_data *)
1157 ctrl_alloc(b, sizeof(struct charclass_data));
1158 ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1159 HELPCTX(selection_charclasses),
1160 charclass_handler, P(ccd));
1161 ccd->listbox->listbox.multisel = 1;
1162 ccd->listbox->listbox.ncols = 4;
1163 ccd->listbox->listbox.percentages = snewn(4, int);
1164 ccd->listbox->listbox.percentages[0] = 15;
1165 ccd->listbox->listbox.percentages[1] = 25;
1166 ccd->listbox->listbox.percentages[2] = 20;
1167 ccd->listbox->listbox.percentages[3] = 40;
1168 ctrl_columns(s, 2, 67, 33);
1169 ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1170 HELPCTX(selection_charclasses),
1171 charclass_handler, P(ccd), P(NULL));
1172 ccd->editbox->generic.column = 0;
1173 ccd->button = ctrl_pushbutton(s, "Set", 's',
1174 HELPCTX(selection_charclasses),
1175 charclass_handler, P(ccd));
1176 ccd->button->generic.column = 1;
1177 ctrl_columns(s, 1, 100);
1178
1179 /*
1180 * The Window/Colours panel.
1181 */
1182 ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1183
1184 s = ctrl_getset(b, "Window/Colours", "general",
1185 "General options for colour usage");
1186 ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1187 HELPCTX(colours_bold),
1188 dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
1189
1190 s = ctrl_getset(b, "Window/Colours", "adjust",
1191 "Adjust the precise colours PuTTY displays");
1192 ctrl_text(s, "Select a colour from the list, and then click the"
1193 " Modify button to change its appearance.",
1194 HELPCTX(colours_config));
1195 ctrl_columns(s, 2, 67, 33);
1196 cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1197 cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1198 HELPCTX(colours_config), colour_handler, P(cd));
1199 cd->listbox->generic.column = 0;
1200 cd->listbox->listbox.height = 7;
1201 c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1202 c->generic.column = 1;
1203 cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
1204 colour_handler, P(cd), P(NULL));
1205 cd->redit->generic.column = 1;
1206 cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
1207 colour_handler, P(cd), P(NULL));
1208 cd->gedit->generic.column = 1;
1209 cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
1210 colour_handler, P(cd), P(NULL));
1211 cd->bedit->generic.column = 1;
1212 cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1213 colour_handler, P(cd));
1214 cd->button->generic.column = 1;
1215 ctrl_columns(s, 1, 100);
1216
1217 /*
1218 * The Connection panel.
1219 */
1220 ctrl_settitle(b, "Connection", "Options controlling the connection");
1221
1222 if (!midsession) {
1223 s = ctrl_getset(b, "Connection", "data", "Data to send to the server");
1224 ctrl_editbox(s, "Terminal-type string", 't', 50,
1225 HELPCTX(connection_termtype),
1226 dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
1227 I(sizeof(((Config *)0)->termtype)));
1228 ctrl_editbox(s, "Auto-login username", 'u', 50,
1229 HELPCTX(connection_username),
1230 dlg_stdeditbox_handler, I(offsetof(Config,username)),
1231 I(sizeof(((Config *)0)->username)));
1232 }
1233
1234 s = ctrl_getset(b, "Connection", "keepalive",
1235 "Sending of null packets to keep session active");
1236 ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1237 HELPCTX(connection_keepalive),
1238 dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
1239 I(-1));
1240
1241 if (!midsession) {
1242 s = ctrl_getset(b, "Connection", "tcp",
1243 "Low-level TCP connection options");
1244 ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)", 'n',
1245 HELPCTX(connection_nodelay),
1246 dlg_stdcheckbox_handler,
1247 I(offsetof(Config,tcp_nodelay)));
1248 }
1249
1250 if (!midsession) {
1251 /*
1252 * The Connection/Proxy panel.
1253 */
1254 ctrl_settitle(b, "Connection/Proxy",
1255 "Options controlling proxy usage");
1256
1257 s = ctrl_getset(b, "Connection/Proxy", "basics", "Proxy basics");
1258 ctrl_radiobuttons(s, "Proxy type:", NO_SHORTCUT, 4,
1259 HELPCTX(proxy_type),
1260 dlg_stdradiobutton_handler,
1261 I(offsetof(Config, proxy_type)),
1262 "None", 'n', I(PROXY_NONE),
1263 "HTTP", 't', I(PROXY_HTTP),
1264 "SOCKS", 's', I(PROXY_SOCKS),
1265 "Telnet", 'l', I(PROXY_TELNET),
1266 NULL);
1267 ctrl_columns(s, 2, 80, 20);
1268 c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1269 HELPCTX(proxy_main),
1270 dlg_stdeditbox_handler,
1271 I(offsetof(Config,proxy_host)),
1272 I(sizeof(((Config *)0)->proxy_host)));
1273 c->generic.column = 0;
1274 c = ctrl_editbox(s, "Port", 'p', 100,
1275 HELPCTX(proxy_main),
1276 dlg_stdeditbox_handler,
1277 I(offsetof(Config,proxy_port)),
1278 I(-1));
1279 c->generic.column = 1;
1280 ctrl_columns(s, 1, 100);
1281 ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1282 HELPCTX(proxy_exclude),
1283 dlg_stdeditbox_handler,
1284 I(offsetof(Config,proxy_exclude_list)),
1285 I(sizeof(((Config *)0)->proxy_exclude_list)));
1286 ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1287 HELPCTX(proxy_exclude),
1288 dlg_stdcheckbox_handler,
1289 I(offsetof(Config,even_proxy_localhost)));
1290 ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1291 HELPCTX(proxy_dns),
1292 dlg_stdradiobutton_handler,
1293 I(offsetof(Config, proxy_dns)),
1294 "No", I(FORCE_OFF),
1295 "Auto", I(AUTO),
1296 "Yes", I(FORCE_ON), NULL);
1297 ctrl_editbox(s, "Username", 'u', 60,
1298 HELPCTX(proxy_auth),
1299 dlg_stdeditbox_handler,
1300 I(offsetof(Config,proxy_username)),
1301 I(sizeof(((Config *)0)->proxy_username)));
1302 c = ctrl_editbox(s, "Password", 'w', 60,
1303 HELPCTX(proxy_auth),
1304 dlg_stdeditbox_handler,
1305 I(offsetof(Config,proxy_password)),
1306 I(sizeof(((Config *)0)->proxy_password)));
1307 c->editbox.password = 1;
1308
1309 s = ctrl_getset(b, "Connection/Proxy", "misc",
1310 "Miscellaneous proxy settings");
1311 ctrl_editbox(s, "Telnet command", 'm', 100,
1312 HELPCTX(proxy_command),
1313 dlg_stdeditbox_handler,
1314 I(offsetof(Config,proxy_telnet_command)),
1315 I(sizeof(((Config *)0)->proxy_telnet_command)));
1316 ctrl_radiobuttons(s, "SOCKS Version", 'v', 2,
1317 HELPCTX(proxy_socksver),
1318 dlg_stdradiobutton_handler,
1319 I(offsetof(Config, proxy_socks_version)),
1320 "Version 5", I(5), "Version 4", I(4), NULL);
1321 }
1322
1323 /*
1324 * The Telnet panel exists in the base config box, and in a
1325 * mid-session reconfig box _if_ we're using Telnet.
1326 */
1327 if (!midsession || protocol == PROT_TELNET) {
1328 /*
1329 * The Connection/Telnet panel.
1330 */
1331 ctrl_settitle(b, "Connection/Telnet",
1332 "Options controlling Telnet connections");
1333
1334 if (!midsession) {
1335 s = ctrl_getset(b, "Connection/Telnet", "data",
1336 "Data to send to the server");
1337 ctrl_editbox(s, "Terminal-speed string", 's', 50,
1338 HELPCTX(telnet_termspeed),
1339 dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1340 I(sizeof(((Config *)0)->termspeed)));
1341 ctrl_text(s, "Environment variables:", HELPCTX(telnet_environ));
1342 ctrl_columns(s, 2, 80, 20);
1343 ed = (struct environ_data *)
1344 ctrl_alloc(b, sizeof(struct environ_data));
1345 ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1346 HELPCTX(telnet_environ),
1347 environ_handler, P(ed), P(NULL));
1348 ed->varbox->generic.column = 0;
1349 ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1350 HELPCTX(telnet_environ),
1351 environ_handler, P(ed), P(NULL));
1352 ed->valbox->generic.column = 0;
1353 ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1354 HELPCTX(telnet_environ),
1355 environ_handler, P(ed));
1356 ed->addbutton->generic.column = 1;
1357 ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1358 HELPCTX(telnet_environ),
1359 environ_handler, P(ed));
1360 ed->rembutton->generic.column = 1;
1361 ctrl_columns(s, 1, 100);
1362 ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1363 HELPCTX(telnet_environ),
1364 environ_handler, P(ed));
1365 ed->listbox->listbox.height = 3;
1366 ed->listbox->listbox.ncols = 2;
1367 ed->listbox->listbox.percentages = snewn(2, int);
1368 ed->listbox->listbox.percentages[0] = 30;
1369 ed->listbox->listbox.percentages[1] = 70;
1370 }
1371
1372 s = ctrl_getset(b, "Connection/Telnet", "protocol",
1373 "Telnet protocol adjustments");
1374
1375 if (!midsession) {
1376 ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
1377 NO_SHORTCUT, 2,
1378 HELPCTX(telnet_oldenviron),
1379 dlg_stdradiobutton_handler,
1380 I(offsetof(Config, rfc_environ)),
1381 "BSD (commonplace)", 'b', I(0),
1382 "RFC 1408 (unusual)", 'f', I(1), NULL);
1383 ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
1384 HELPCTX(telnet_passive),
1385 dlg_stdradiobutton_handler,
1386 I(offsetof(Config, passive_telnet)),
1387 "Passive", I(1), "Active", I(0), NULL);
1388 }
1389 ctrl_checkbox(s, "Keyboard sends telnet Backspace and Interrupt", 'k',
1390 HELPCTX(telnet_specialkeys),
1391 dlg_stdcheckbox_handler,
1392 I(offsetof(Config,telnet_keyboard)));
1393 ctrl_checkbox(s, "Return key sends telnet New Line instead of ^M",
1394 NO_SHORTCUT, HELPCTX(telnet_newline),
1395 dlg_stdcheckbox_handler,
1396 I(offsetof(Config,telnet_newline)));
1397 }
1398
1399 if (!midsession) {
1400
1401 /*
1402 * The Connection/Rlogin panel.
1403 */
1404 ctrl_settitle(b, "Connection/Rlogin",
1405 "Options controlling Rlogin connections");
1406
1407 s = ctrl_getset(b, "Connection/Rlogin", "data",
1408 "Data to send to the server");
1409 ctrl_editbox(s, "Terminal-speed string", 's', 50,
1410 HELPCTX(rlogin_termspeed),
1411 dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1412 I(sizeof(((Config *)0)->termspeed)));
1413 ctrl_editbox(s, "Local username:", 'l', 50,
1414 HELPCTX(rlogin_localuser),
1415 dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
1416 I(sizeof(((Config *)0)->localusername)));
1417
1418 }
1419
1420 /*
1421 * All the SSH stuff is omitted in PuTTYtel.
1422 */
1423
1424 if (!midsession && backends[3].name != NULL) {
1425
1426 /*
1427 * The Connection/SSH panel.
1428 */
1429 ctrl_settitle(b, "Connection/SSH",
1430 "Options controlling SSH connections");
1431
1432 s = ctrl_getset(b, "Connection/SSH", "data",
1433 "Data to send to the server");
1434 ctrl_editbox(s, "Remote command:", 'r', 100,
1435 HELPCTX(ssh_command),
1436 dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
1437 I(sizeof(((Config *)0)->remote_cmd)));
1438
1439 s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1440 ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
1441 HELPCTX(ssh_nopty),
1442 dlg_stdcheckbox_handler,
1443 I(offsetof(Config,nopty)));
1444 ctrl_checkbox(s, "Enable compression", 'e',
1445 HELPCTX(ssh_compress),
1446 dlg_stdcheckbox_handler,
1447 I(offsetof(Config,compression)));
1448 ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
1449 HELPCTX(ssh_protocol),
1450 dlg_stdradiobutton_handler,
1451 I(offsetof(Config, sshprot)),
1452 "1 only", 'l', I(0),
1453 "1", '1', I(1),
1454 "2", '2', I(2),
1455 "2 only", 'n', I(3), NULL);
1456
1457 s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
1458 c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
1459 HELPCTX(ssh_ciphers),
1460 cipherlist_handler, P(NULL));
1461 c->listbox.height = 6;
1462
1463 ctrl_checkbox(s, "Enable non-standard use of single-DES in SSH 2", 'i',
1464 HELPCTX(ssh_ciphers),
1465 dlg_stdcheckbox_handler,
1466 I(offsetof(Config,ssh2_des_cbc)));
1467
1468 /*
1469 * The Connection/SSH/Auth panel.
1470 */
1471 ctrl_settitle(b, "Connection/SSH/Auth",
1472 "Options controlling SSH authentication");
1473
1474 s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
1475 "Authentication methods");
1476 ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH1)", 'm',
1477 HELPCTX(ssh_auth_tis),
1478 dlg_stdcheckbox_handler,
1479 I(offsetof(Config,try_tis_auth)));
1480 ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH2)",
1481 'i', HELPCTX(ssh_auth_ki),
1482 dlg_stdcheckbox_handler,
1483 I(offsetof(Config,try_ki_auth)));
1484
1485 s = ctrl_getset(b, "Connection/SSH/Auth", "params",
1486 "Authentication parameters");
1487 ctrl_checkbox(s, "Allow agent forwarding", 'f',
1488 HELPCTX(ssh_auth_agentfwd),
1489 dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
1490 ctrl_checkbox(s, "Allow attempted changes of username in SSH2", 'u',
1491 HELPCTX(ssh_auth_changeuser),
1492 dlg_stdcheckbox_handler,
1493 I(offsetof(Config,change_username)));
1494 ctrl_filesel(s, "Private key file for authentication:", 'k',
1495 FILTER_KEY_FILES, FALSE, "Select private key file",
1496 HELPCTX(ssh_auth_privkey),
1497 dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
1498
1499 /*
1500 * The Connection/SSH/Tunnels panel.
1501 */
1502 ctrl_settitle(b, "Connection/SSH/Tunnels",
1503 "Options controlling SSH tunnelling");
1504
1505 s = ctrl_getset(b, "Connection/SSH/Tunnels", "x11", "X11 forwarding");
1506 ctrl_checkbox(s, "Enable X11 forwarding", 'e',
1507 HELPCTX(ssh_tunnels_x11),
1508 dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
1509 ctrl_editbox(s, "X display location", 'x', 50,
1510 HELPCTX(ssh_tunnels_x11),
1511 dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
1512 I(sizeof(((Config *)0)->x11_display)));
1513 ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
1514 HELPCTX(ssh_tunnels_x11auth),
1515 dlg_stdradiobutton_handler,
1516 I(offsetof(Config, x11_auth)),
1517 "MIT-Magic-Cookie-1", I(X11_MIT),
1518 "XDM-Authorization-1", I(X11_XDM), NULL);
1519
1520 s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
1521 "Port forwarding");
1522 ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
1523 HELPCTX(ssh_tunnels_portfwd_localhost),
1524 dlg_stdcheckbox_handler,
1525 I(offsetof(Config,lport_acceptall)));
1526 ctrl_checkbox(s, "Remote ports do the same (SSH v2 only)", 'p',
1527 HELPCTX(ssh_tunnels_portfwd_localhost),
1528 dlg_stdcheckbox_handler,
1529 I(offsetof(Config,rport_acceptall)));
1530
1531 ctrl_columns(s, 3, 55, 20, 25);
1532 c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
1533 c->generic.column = COLUMN_FIELD(0,2);
1534 /* You want to select from the list, _then_ hit Remove. So tab order
1535 * should be that way round. */
1536 pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
1537 pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1538 HELPCTX(ssh_tunnels_portfwd),
1539 portfwd_handler, P(pfd));
1540 pfd->rembutton->generic.column = 2;
1541 pfd->rembutton->generic.tabdelay = 1;
1542 pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1543 HELPCTX(ssh_tunnels_portfwd),
1544 portfwd_handler, P(pfd));
1545 pfd->listbox->listbox.height = 3;
1546 pfd->listbox->listbox.ncols = 2;
1547 pfd->listbox->listbox.percentages = snewn(2, int);
1548 pfd->listbox->listbox.percentages[0] = 20;
1549 pfd->listbox->listbox.percentages[1] = 80;
1550 ctrl_tabdelay(s, pfd->rembutton);
1551 ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
1552 /* You want to enter source, destination and type, _then_ hit Add.
1553 * Again, we adjust the tab order to reflect this. */
1554 pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
1555 HELPCTX(ssh_tunnels_portfwd),
1556 portfwd_handler, P(pfd));
1557 pfd->addbutton->generic.column = 2;
1558 pfd->addbutton->generic.tabdelay = 1;
1559 pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
1560 HELPCTX(ssh_tunnels_portfwd),
1561 portfwd_handler, P(pfd), P(NULL));
1562 pfd->sourcebox->generic.column = 0;
1563 pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
1564 HELPCTX(ssh_tunnels_portfwd),
1565 portfwd_handler, P(pfd), P(NULL));
1566 pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
1567 HELPCTX(ssh_tunnels_portfwd),
1568 portfwd_handler, P(pfd),
1569 "Local", 'l', P(NULL),
1570 "Remote", 'm', P(NULL),
1571 "Dynamic", 'y', P(NULL),
1572 NULL);
1573 ctrl_tabdelay(s, pfd->addbutton);
1574 ctrl_columns(s, 1, 100);
1575
1576 /*
1577 * The Connection/SSH/Bugs panel.
1578 */
1579 ctrl_settitle(b, "Connection/SSH/Bugs",
1580 "Workarounds for SSH server bugs");
1581
1582 s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
1583 "Detection of known bugs in SSH servers");
1584 ctrl_droplist(s, "Chokes on SSH1 ignore messages", 'i', 20,
1585 HELPCTX(ssh_bugs_ignore1),
1586 sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
1587 ctrl_droplist(s, "Refuses all SSH1 password camouflage", 's', 20,
1588 HELPCTX(ssh_bugs_plainpw1),
1589 sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
1590 ctrl_droplist(s, "Chokes on SSH1 RSA authentication", 'r', 20,
1591 HELPCTX(ssh_bugs_rsa1),
1592 sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
1593 ctrl_droplist(s, "Miscomputes SSH2 HMAC keys", 'm', 20,
1594 HELPCTX(ssh_bugs_hmac2),
1595 sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
1596 ctrl_droplist(s, "Miscomputes SSH2 encryption keys", 'e', 20,
1597 HELPCTX(ssh_bugs_derivekey2),
1598 sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
1599 ctrl_droplist(s, "Requires padding on SSH2 RSA signatures", 'p', 20,
1600 HELPCTX(ssh_bugs_rsapad2),
1601 sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
1602 ctrl_droplist(s, "Chokes on Diffie-Hellman group exchange", 'd', 20,
1603 HELPCTX(ssh_bugs_dhgex2),
1604 sshbug_handler, I(offsetof(Config,sshbug_dhgex2)));
1605 ctrl_droplist(s, "Misuses the session ID in PK auth", 'n', 20,
1606 HELPCTX(ssh_bugs_pksessid2),
1607 sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
1608 }
1609 }