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