]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Unix can't sensibly enumerate printers (since they're defined as
[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_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 }