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