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