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