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