]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - config.c
Windows sets automatic tabs in list boxes, but GTK doesn't, so we
[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(-TICKSPERSEC));
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(-TICKSPERSEC));
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, "Reset scrollback on keypress", 'k',
990                   HELPCTX(window_scrollback),
991                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));
992     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
993                   HELPCTX(window_scrollback),
994                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));
995     ctrl_checkbox(s, "Push erased text into scrollback", 'e',
996                   HELPCTX(window_erased),
997                   dlg_stdcheckbox_handler,
998                   I(offsetof(Config,erase_to_scrollback)));
999
1000     /*
1001      * The Window/Appearance panel.
1002      */
1003     ctrl_settitle(b, "Window/Appearance",
1004                   "Configure the appearance of PuTTY's window");
1005
1006     s = ctrl_getset(b, "Window/Appearance", "cursor",
1007                     "Adjust the use of the cursor");
1008     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
1009                       HELPCTX(appearance_cursor),
1010                       dlg_stdradiobutton_handler,
1011                       I(offsetof(Config, cursor_type)),
1012                       "Block", 'l', I(0),
1013                       "Underline", 'u', I(1),
1014                       "Vertical line", 'v', I(2), NULL);
1015     ctrl_checkbox(s, "Cursor blinks", 'b',
1016                   HELPCTX(appearance_cursor),
1017                   dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));
1018
1019     s = ctrl_getset(b, "Window/Appearance", "font",
1020                     "Font settings");
1021     ctrl_fontsel(s, "Font used in the terminal window", 'n',
1022                  HELPCTX(appearance_font),
1023                  dlg_stdfontsel_handler, I(offsetof(Config, font)));
1024
1025     s = ctrl_getset(b, "Window/Appearance", "mouse",
1026                     "Adjust the use of the mouse pointer");
1027     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
1028                   HELPCTX(appearance_hidemouse),
1029                   dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));
1030
1031     s = ctrl_getset(b, "Window/Appearance", "border",
1032                     "Adjust the window border");
1033     ctrl_editbox(s, "Gap between text and window edge:", NO_SHORTCUT, 20,
1034                  HELPCTX(appearance_border),
1035                  dlg_stdeditbox_handler,
1036                  I(offsetof(Config,window_border)), I(-1));
1037
1038     /*
1039      * The Window/Behaviour panel.
1040      */
1041     ctrl_settitle(b, "Window/Behaviour",
1042                   "Configure the behaviour of PuTTY's window");
1043
1044     s = ctrl_getset(b, "Window/Behaviour", "title",
1045                     "Adjust the behaviour of the window title");
1046     ctrl_editbox(s, "Window title:", 't', 100,
1047                  HELPCTX(appearance_title),
1048                  dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),
1049                  I(sizeof(((Config *)0)->wintitle)));
1050     ctrl_checkbox(s, "Separate window and icon titles", 'i',
1051                   HELPCTX(appearance_title),
1052                   dlg_stdcheckbox_handler,
1053                   I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));
1054
1055     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
1056     ctrl_checkbox(s, "Warn before closing window", 'w',
1057                   HELPCTX(behaviour_closewarn),
1058                   dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));
1059
1060     /*
1061      * The Window/Translation panel.
1062      */
1063     ctrl_settitle(b, "Window/Translation",
1064                   "Options controlling character set translation");
1065
1066     s = ctrl_getset(b, "Window/Translation", "trans",
1067                     "Character set translation on received data");
1068     ctrl_combobox(s, "Received data assumed to be in which character set:",
1069                   'r', 100, HELPCTX(translation_codepage),
1070                   codepage_handler, P(NULL), P(NULL));
1071
1072     s = ctrl_getset(b, "Window/Translation", "linedraw",
1073                     "Adjust how PuTTY displays line drawing characters");
1074     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,
1075                       HELPCTX(translation_linedraw),
1076                       dlg_stdradiobutton_handler,
1077                       I(offsetof(Config, vtmode)),
1078                       "Font has XWindows encoding", 'x', I(VT_XWINDOWS),
1079                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),
1080                       "Unicode mode", 'u', I(VT_UNICODE), NULL);
1081
1082     /*
1083      * The Window/Selection panel.
1084      */
1085     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
1086
1087     s = ctrl_getset(b, "Window/Selection", "trans",
1088                     "Translation of pasted characters");
1089     ctrl_checkbox(s, "Don't translate line drawing chars into +, - and |",'d',
1090                   HELPCTX(selection_linedraw),
1091                   dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));
1092         
1093     s = ctrl_getset(b, "Window/Selection", "mouse",
1094                     "Control use of mouse");
1095     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
1096                   HELPCTX(selection_shiftdrag),
1097                   dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));
1098     ctrl_radiobuttons(s,
1099                       "Default selection mode (Alt+drag does the other one):",
1100                       NO_SHORTCUT, 2,
1101                       HELPCTX(selection_rect),
1102                       dlg_stdradiobutton_handler,
1103                       I(offsetof(Config, rect_select)),
1104                       "Normal", 'n', I(0),
1105                       "Rectangular block", 'r', I(1), NULL);
1106
1107     s = ctrl_getset(b, "Window/Selection", "charclass",
1108                     "Control the select-one-word-at-a-time mode");
1109     ccd = (struct charclass_data *)
1110         ctrl_alloc(b, sizeof(struct charclass_data));
1111     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
1112                                 HELPCTX(selection_charclasses),
1113                                 charclass_handler, P(ccd));
1114     ccd->listbox->listbox.multisel = 1;
1115     ccd->listbox->listbox.ncols = 4;
1116     ccd->listbox->listbox.percentages = smalloc(4*sizeof(int));
1117     ccd->listbox->listbox.percentages[0] = 15;
1118     ccd->listbox->listbox.percentages[1] = 25;
1119     ccd->listbox->listbox.percentages[2] = 20;
1120     ccd->listbox->listbox.percentages[3] = 40;
1121     ctrl_columns(s, 2, 67, 33);
1122     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
1123                                 HELPCTX(selection_charclasses),
1124                                 charclass_handler, P(ccd), P(NULL));
1125     ccd->editbox->generic.column = 0;
1126     ccd->button = ctrl_pushbutton(s, "Set", 's',
1127                                   HELPCTX(selection_charclasses),
1128                                   charclass_handler, P(ccd));
1129     ccd->button->generic.column = 1;
1130     ctrl_columns(s, 1, 100);
1131
1132     /*
1133      * The Window/Colours panel.
1134      */
1135     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
1136
1137     s = ctrl_getset(b, "Window/Colours", "general",
1138                     "General options for colour usage");
1139     ctrl_checkbox(s, "Bolded text is a different colour", 'b',
1140                   HELPCTX(colours_bold),
1141                   dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));
1142
1143     s = ctrl_getset(b, "Window/Colours", "adjust",
1144                     "Adjust the precise colours PuTTY displays");
1145     ctrl_text(s, "Select a colour from the list, and then click the"
1146               " Modify button to change its appearance.",
1147               HELPCTX(colours_config));
1148     ctrl_columns(s, 2, 67, 33);
1149     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
1150     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
1151                                HELPCTX(colours_config), colour_handler, P(cd));
1152     cd->listbox->generic.column = 0;
1153     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
1154     c->generic.column = 1;
1155     cd->rgbtext = ctrl_text(s, "00/00/00", HELPCTX(colours_config));
1156     cd->rgbtext->generic.column = 1;
1157     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
1158                                  colour_handler, P(cd));
1159     cd->button->generic.column = 1;
1160     ctrl_columns(s, 1, 100);
1161
1162     /*
1163      * The Connection panel.
1164      */
1165     ctrl_settitle(b, "Connection", "Options controlling the connection");
1166
1167     if (!midsession) {
1168         s = ctrl_getset(b, "Connection", "data", "Data to send to the server");
1169         ctrl_editbox(s, "Terminal-type string", 't', 50,
1170                      HELPCTX(connection_termtype),
1171                      dlg_stdeditbox_handler, I(offsetof(Config,termtype)),
1172                      I(sizeof(((Config *)0)->termtype)));
1173         ctrl_editbox(s, "Auto-login username", 'u', 50,
1174                      HELPCTX(connection_username),
1175                      dlg_stdeditbox_handler, I(offsetof(Config,username)),
1176                      I(sizeof(((Config *)0)->username)));
1177     }
1178
1179     s = ctrl_getset(b, "Connection", "keepalive",
1180                     "Sending of null packets to keep session active");
1181     ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
1182                  HELPCTX(connection_keepalive),
1183                  dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),
1184                  I(-1));
1185
1186     if (!midsession) {
1187         s = ctrl_getset(b, "Connection", "tcp",
1188                         "Low-level TCP connection options");
1189         ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)", 'n',
1190                       HELPCTX(connection_nodelay),
1191                       dlg_stdcheckbox_handler,
1192                       I(offsetof(Config,tcp_nodelay)));
1193     }
1194
1195     if (!midsession) {
1196         /*
1197          * The Connection/Proxy panel.
1198          */
1199         ctrl_settitle(b, "Connection/Proxy",
1200                       "Options controlling proxy usage");
1201
1202         s = ctrl_getset(b, "Connection/Proxy", "basics", "Proxy basics");
1203         ctrl_radiobuttons(s, "Proxy type:", NO_SHORTCUT, 4,
1204                           HELPCTX(proxy_type),
1205                           dlg_stdradiobutton_handler,
1206                           I(offsetof(Config, proxy_type)),
1207                           "None", 'n', I(PROXY_NONE),
1208                           "HTTP", 't', I(PROXY_HTTP),
1209                           "SOCKS", 's', I(PROXY_SOCKS),
1210                           "Telnet", 'l', I(PROXY_TELNET),
1211                           NULL);
1212         ctrl_columns(s, 2, 80, 20);
1213         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
1214                          HELPCTX(proxy_main),
1215                          dlg_stdeditbox_handler,
1216                          I(offsetof(Config,proxy_host)),
1217                          I(sizeof(((Config *)0)->proxy_host)));
1218         c->generic.column = 0;
1219         c = ctrl_editbox(s, "Port", 'p', 100,
1220                          HELPCTX(proxy_main),
1221                          dlg_stdeditbox_handler,
1222                          I(offsetof(Config,proxy_port)),
1223                          I(-1));
1224         c->generic.column = 1;
1225         ctrl_columns(s, 1, 100);
1226         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
1227                      HELPCTX(proxy_exclude),
1228                      dlg_stdeditbox_handler,
1229                      I(offsetof(Config,proxy_exclude_list)),
1230                      I(sizeof(((Config *)0)->proxy_exclude_list)));
1231         ctrl_checkbox(s, "Consider proxying local host connections", 'x',
1232                       HELPCTX(proxy_exclude),
1233                       dlg_stdcheckbox_handler,
1234                       I(offsetof(Config,even_proxy_localhost)));
1235         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
1236                           HELPCTX(proxy_dns),
1237                           dlg_stdradiobutton_handler,
1238                           I(offsetof(Config, proxy_dns)),
1239                           "No", I(FORCE_OFF),
1240                           "Auto", I(AUTO),
1241                           "Yes", I(FORCE_ON), NULL);
1242         ctrl_editbox(s, "Username", 'u', 60,
1243                      HELPCTX(proxy_auth),
1244                      dlg_stdeditbox_handler,
1245                      I(offsetof(Config,proxy_username)),
1246                      I(sizeof(((Config *)0)->proxy_username)));
1247         c = ctrl_editbox(s, "Password", 'w', 60,
1248                          HELPCTX(proxy_auth),
1249                          dlg_stdeditbox_handler,
1250                          I(offsetof(Config,proxy_password)),
1251                          I(sizeof(((Config *)0)->proxy_password)));
1252         c->editbox.password = 1;
1253
1254         s = ctrl_getset(b, "Connection/Proxy", "misc",
1255                         "Miscellaneous proxy settings");
1256         ctrl_editbox(s, "Telnet command", 'm', 100,
1257                      HELPCTX(proxy_command),
1258                      dlg_stdeditbox_handler,
1259                      I(offsetof(Config,proxy_telnet_command)),
1260                      I(sizeof(((Config *)0)->proxy_telnet_command)));
1261         ctrl_radiobuttons(s, "SOCKS Version", 'v', 2,
1262                           HELPCTX(proxy_socksver),
1263                           dlg_stdradiobutton_handler,
1264                           I(offsetof(Config, proxy_socks_version)),
1265                           "Version 5", I(5), "Version 4", I(4), NULL);
1266     }
1267
1268     /*
1269      * The Telnet panel exists in the base config box, and in a
1270      * mid-session reconfig box _if_ we're using Telnet.
1271      */
1272     if (!midsession || protocol == PROT_TELNET) {
1273         /*
1274          * The Connection/Telnet panel.
1275          */
1276         ctrl_settitle(b, "Connection/Telnet",
1277                       "Options controlling Telnet connections");
1278
1279         if (!midsession) {
1280             s = ctrl_getset(b, "Connection/Telnet", "data",
1281                             "Data to send to the server");
1282             ctrl_editbox(s, "Terminal-speed string", 's', 50,
1283                          HELPCTX(telnet_termspeed),
1284                          dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1285                          I(sizeof(((Config *)0)->termspeed)));
1286             ctrl_text(s, "Environment variables:", HELPCTX(telnet_environ));
1287             ctrl_columns(s, 2, 80, 20);
1288             ed = (struct environ_data *)
1289                 ctrl_alloc(b, sizeof(struct environ_data));
1290             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
1291                                       HELPCTX(telnet_environ),
1292                                       environ_handler, P(ed), P(NULL));
1293             ed->varbox->generic.column = 0;
1294             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
1295                                       HELPCTX(telnet_environ),
1296                                       environ_handler, P(ed), P(NULL));
1297             ed->valbox->generic.column = 0;
1298             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
1299                                             HELPCTX(telnet_environ),
1300                                             environ_handler, P(ed));
1301             ed->addbutton->generic.column = 1;
1302             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1303                                             HELPCTX(telnet_environ),
1304                                             environ_handler, P(ed));
1305             ed->rembutton->generic.column = 1;
1306             ctrl_columns(s, 1, 100);
1307             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1308                                        HELPCTX(telnet_environ),
1309                                        environ_handler, P(ed));
1310             ed->listbox->listbox.height = 3;
1311         }
1312
1313         s = ctrl_getset(b, "Connection/Telnet", "protocol",
1314                         "Telnet protocol adjustments");
1315
1316         if (!midsession) {
1317             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
1318                               NO_SHORTCUT, 2,
1319                               HELPCTX(telnet_oldenviron),
1320                               dlg_stdradiobutton_handler,
1321                               I(offsetof(Config, rfc_environ)),
1322                               "BSD (commonplace)", 'b', I(0),
1323                               "RFC 1408 (unusual)", 'f', I(1), NULL);
1324             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
1325                               HELPCTX(telnet_passive),
1326                               dlg_stdradiobutton_handler,
1327                               I(offsetof(Config, passive_telnet)),
1328                               "Passive", I(1), "Active", I(0), NULL);
1329         }
1330         ctrl_checkbox(s, "Keyboard sends telnet Backspace and Interrupt", 'k',
1331                       HELPCTX(telnet_specialkeys),
1332                       dlg_stdcheckbox_handler,
1333                       I(offsetof(Config,telnet_keyboard)));
1334         ctrl_checkbox(s, "Return key sends telnet New Line instead of ^M",
1335                       NO_SHORTCUT, HELPCTX(telnet_newline),
1336                       dlg_stdcheckbox_handler,
1337                       I(offsetof(Config,telnet_newline)));
1338     }
1339
1340     if (!midsession) {
1341
1342         /*
1343          * The Connection/Rlogin panel.
1344          */
1345         ctrl_settitle(b, "Connection/Rlogin",
1346                       "Options controlling Rlogin connections");
1347
1348         s = ctrl_getset(b, "Connection/Rlogin", "data",
1349                         "Data to send to the server");
1350         ctrl_editbox(s, "Terminal-speed string", 's', 50,
1351                      HELPCTX(rlogin_termspeed),
1352                      dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),
1353                      I(sizeof(((Config *)0)->termspeed)));
1354         ctrl_editbox(s, "Local username:", 'l', 50,
1355                      HELPCTX(rlogin_localuser),
1356                      dlg_stdeditbox_handler, I(offsetof(Config,localusername)),
1357                      I(sizeof(((Config *)0)->localusername)));
1358
1359     }
1360
1361     /*
1362      * All the SSH stuff is omitted in PuTTYtel.
1363      */
1364
1365     if (!midsession && backends[3].name != NULL) {
1366
1367         /*
1368          * The Connection/SSH panel.
1369          */
1370         ctrl_settitle(b, "Connection/SSH",
1371                       "Options controlling SSH connections");
1372
1373         s = ctrl_getset(b, "Connection/SSH", "data",
1374                         "Data to send to the server");
1375         ctrl_editbox(s, "Remote command:", 'r', 100,
1376                      HELPCTX(ssh_command),
1377                      dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
1378                      I(sizeof(((Config *)0)->remote_cmd)));
1379
1380         s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
1381         ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
1382                       HELPCTX(ssh_nopty),
1383                       dlg_stdcheckbox_handler,
1384                       I(offsetof(Config,nopty)));
1385         ctrl_checkbox(s, "Enable compression", 'e',
1386                       HELPCTX(ssh_compress),
1387                       dlg_stdcheckbox_handler,
1388                       I(offsetof(Config,compression)));
1389         ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
1390                           HELPCTX(ssh_protocol),
1391                           dlg_stdradiobutton_handler,
1392                           I(offsetof(Config, sshprot)),
1393                           "1 only", 'l', I(0),
1394                           "1", '1', I(1),
1395                           "2", '2', I(2),
1396                           "2 only", 'n', I(3), NULL);
1397
1398         s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
1399         c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
1400                           HELPCTX(ssh_ciphers),
1401                           cipherlist_handler, P(NULL));
1402         c->listbox.height = 6;
1403         
1404         ctrl_checkbox(s, "Enable non-standard use of single-DES in SSH 2", 'i',
1405                       HELPCTX(ssh_ciphers),
1406                       dlg_stdcheckbox_handler,
1407                       I(offsetof(Config,ssh2_des_cbc)));
1408
1409         /*
1410          * The Connection/SSH/Auth panel.
1411          */
1412         ctrl_settitle(b, "Connection/SSH/Auth",
1413                       "Options controlling SSH authentication");
1414
1415         s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
1416                         "Authentication methods");
1417         ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH1)", 'm',
1418                       HELPCTX(ssh_auth_tis),
1419                       dlg_stdcheckbox_handler,
1420                       I(offsetof(Config,try_tis_auth)));
1421         ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH2)",
1422                       'i', HELPCTX(ssh_auth_ki),
1423                       dlg_stdcheckbox_handler,
1424                       I(offsetof(Config,try_ki_auth)));
1425
1426         s = ctrl_getset(b, "Connection/SSH/Auth", "params",
1427                         "Authentication parameters");
1428         ctrl_checkbox(s, "Allow agent forwarding", 'f',
1429                       HELPCTX(ssh_auth_agentfwd),
1430                       dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
1431         ctrl_checkbox(s, "Allow attempted changes of username in SSH2", 'u',
1432                       HELPCTX(ssh_auth_changeuser),
1433                       dlg_stdcheckbox_handler,
1434                       I(offsetof(Config,change_username)));
1435         ctrl_filesel(s, "Private key file for authentication:", 'k',
1436                      FILTER_KEY_FILES, FALSE, "Select private key file",
1437                      HELPCTX(ssh_auth_privkey),
1438                      dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
1439
1440         /*
1441          * The Connection/SSH/Tunnels panel.
1442          */
1443         ctrl_settitle(b, "Connection/SSH/Tunnels",
1444                       "Options controlling SSH tunnelling");
1445
1446         s = ctrl_getset(b, "Connection/SSH/Tunnels", "x11", "X11 forwarding");
1447         ctrl_checkbox(s, "Enable X11 forwarding", 'e',
1448                       HELPCTX(ssh_tunnels_x11),
1449                       dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
1450         ctrl_editbox(s, "X display location", 'x', 50,
1451                      HELPCTX(ssh_tunnels_x11),
1452                      dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
1453                      I(sizeof(((Config *)0)->x11_display)));
1454         ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
1455                           HELPCTX(ssh_tunnels_x11auth),
1456                           dlg_stdradiobutton_handler,
1457                           I(offsetof(Config, x11_auth)),
1458                           "MIT-Magic-Cookie-1", I(X11_MIT),
1459                           "XDM-Authorization-1", I(X11_XDM), NULL);
1460
1461         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
1462                         "Port forwarding");
1463         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
1464                       HELPCTX(ssh_tunnels_portfwd_localhost),
1465                       dlg_stdcheckbox_handler,
1466                       I(offsetof(Config,lport_acceptall)));
1467         ctrl_checkbox(s, "Remote ports do the same (SSH v2 only)", 'p',
1468                       HELPCTX(ssh_tunnels_portfwd_localhost),
1469                       dlg_stdcheckbox_handler,
1470                       I(offsetof(Config,rport_acceptall)));
1471
1472         ctrl_columns(s, 3, 55, 20, 25);
1473         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
1474         c->generic.column = COLUMN_FIELD(0,2);
1475         /* You want to select from the list, _then_ hit Remove. So tab order
1476          * should be that way round. */
1477         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
1478         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
1479                                          HELPCTX(ssh_tunnels_portfwd),
1480                                          portfwd_handler, P(pfd));
1481         pfd->rembutton->generic.column = 2;
1482         pfd->rembutton->generic.tabdelay = 1;
1483         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
1484                                     HELPCTX(ssh_tunnels_portfwd),
1485                                     portfwd_handler, P(pfd));
1486         pfd->listbox->listbox.height = 3;
1487         pfd->listbox->listbox.ncols = 2;
1488         pfd->listbox->listbox.percentages = smalloc(2*sizeof(int));
1489         pfd->listbox->listbox.percentages[0] = 20;
1490         pfd->listbox->listbox.percentages[1] = 80;
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 }