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