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