OSDN Git Service

Add support for retrying file transfer.
[ffftp/ffftp.git] / putty / CONFIG.C
1 /*\r
2  * config.c - the platform-independent parts of the PuTTY\r
3  * configuration box.\r
4  */\r
5 \r
6 #include <assert.h>\r
7 #include <stdlib.h>\r
8 \r
9 #include "putty.h"\r
10 #include "dialog.h"\r
11 #include "storage.h"\r
12 \r
13 #define PRINTER_DISABLED_STRING "None (printing disabled)"\r
14 \r
15 #define HOST_BOX_TITLE "Host Name (or IP address)"\r
16 #define PORT_BOX_TITLE "Port"\r
17 \r
18 static void config_host_handler(union control *ctrl, void *dlg,\r
19                                 void *data, int event)\r
20 {\r
21     Config *cfg = (Config *)data;\r
22 \r
23     /*\r
24      * This function works just like the standard edit box handler,\r
25      * only it has to choose the control's label and text from two\r
26      * different places depending on the protocol.\r
27      */\r
28     if (event == EVENT_REFRESH) {\r
29         if (cfg->protocol == PROT_SERIAL) {\r
30             /*\r
31              * This label text is carefully chosen to contain an n,\r
32              * since that's the shortcut for the host name control.\r
33              */\r
34             dlg_label_change(ctrl, dlg, "Serial line");\r
35             dlg_editbox_set(ctrl, dlg, cfg->serline);\r
36         } else {\r
37             dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);\r
38             dlg_editbox_set(ctrl, dlg, cfg->host);\r
39         }\r
40     } else if (event == EVENT_VALCHANGE) {\r
41         if (cfg->protocol == PROT_SERIAL)\r
42             dlg_editbox_get(ctrl, dlg, cfg->serline, lenof(cfg->serline));\r
43         else\r
44             dlg_editbox_get(ctrl, dlg, cfg->host, lenof(cfg->host));\r
45     }\r
46 }\r
47 \r
48 static void config_port_handler(union control *ctrl, void *dlg,\r
49                                 void *data, int event)\r
50 {\r
51     Config *cfg = (Config *)data;\r
52     char buf[80];\r
53 \r
54     /*\r
55      * This function works similarly to the standard edit box handler,\r
56      * only it has to choose the control's label and text from two\r
57      * different places depending on the protocol.\r
58      */\r
59     if (event == EVENT_REFRESH) {\r
60         if (cfg->protocol == PROT_SERIAL) {\r
61             /*\r
62              * This label text is carefully chosen to contain a p,\r
63              * since that's the shortcut for the port control.\r
64              */\r
65             dlg_label_change(ctrl, dlg, "Speed");\r
66             sprintf(buf, "%d", cfg->serspeed);\r
67         } else {\r
68             dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);\r
69             if (cfg->port != 0)\r
70                 sprintf(buf, "%d", cfg->port);\r
71             else\r
72                 /* Display an (invalid) port of 0 as blank */\r
73                 buf[0] = '\0';\r
74         }\r
75         dlg_editbox_set(ctrl, dlg, buf);\r
76     } else if (event == EVENT_VALCHANGE) {\r
77         dlg_editbox_get(ctrl, dlg, buf, lenof(buf));\r
78         if (cfg->protocol == PROT_SERIAL)\r
79             cfg->serspeed = atoi(buf);\r
80         else\r
81             cfg->port = atoi(buf);\r
82     }\r
83 }\r
84 \r
85 struct hostport {\r
86     union control *host, *port;\r
87 };\r
88 \r
89 /*\r
90  * We export this function so that platform-specific config\r
91  * routines can use it to conveniently identify the protocol radio\r
92  * buttons in order to add to them.\r
93  */\r
94 void config_protocolbuttons_handler(union control *ctrl, void *dlg,\r
95                                     void *data, int event)\r
96 {\r
97     int button;\r
98     Config *cfg = (Config *)data;\r
99     struct hostport *hp = (struct hostport *)ctrl->radio.context.p;\r
100 \r
101     /*\r
102      * This function works just like the standard radio-button\r
103      * handler, except that it also has to change the setting of\r
104      * the port box, and refresh both host and port boxes when. We\r
105      * expect the context parameter to point at a hostport\r
106      * structure giving the `union control's for both.\r
107      */\r
108     if (event == EVENT_REFRESH) {\r
109         for (button = 0; button < ctrl->radio.nbuttons; button++)\r
110             if (cfg->protocol == ctrl->radio.buttondata[button].i)\r
111                 break;\r
112         /* We expected that `break' to happen, in all circumstances. */\r
113         assert(button < ctrl->radio.nbuttons);\r
114         dlg_radiobutton_set(ctrl, dlg, button);\r
115     } else if (event == EVENT_VALCHANGE) {\r
116         int oldproto = cfg->protocol;\r
117         button = dlg_radiobutton_get(ctrl, dlg);\r
118         assert(button >= 0 && button < ctrl->radio.nbuttons);\r
119         cfg->protocol = ctrl->radio.buttondata[button].i;\r
120         if (oldproto != cfg->protocol) {\r
121             Backend *ob = backend_from_proto(oldproto);\r
122             Backend *nb = backend_from_proto(cfg->protocol);\r
123             assert(ob);\r
124             assert(nb);\r
125             /* Iff the user hasn't changed the port from the old protocol's\r
126              * default, update it with the new protocol's default.\r
127              * (This includes a "default" of 0, implying that there is no\r
128              * sensible default for that protocol; in this case it's\r
129              * displayed as a blank.)\r
130              * This helps with the common case of tabbing through the\r
131              * controls in order and setting a non-default port before\r
132              * getting to the protocol; we want that non-default port\r
133              * to be preserved. */\r
134             if (cfg->port == ob->default_port)\r
135                 cfg->port = nb->default_port;\r
136         }\r
137         dlg_refresh(hp->host, dlg);\r
138         dlg_refresh(hp->port, dlg);\r
139     }\r
140 }\r
141 \r
142 static void loggingbuttons_handler(union control *ctrl, void *dlg,\r
143                                    void *data, int event)\r
144 {\r
145     int button;\r
146     Config *cfg = (Config *)data;\r
147     /* This function works just like the standard radio-button handler,\r
148      * but it has to fall back to "no logging" in situations where the\r
149      * configured logging type isn't applicable.\r
150      */\r
151     if (event == EVENT_REFRESH) {\r
152         for (button = 0; button < ctrl->radio.nbuttons; button++)\r
153             if (cfg->logtype == ctrl->radio.buttondata[button].i)\r
154                 break;\r
155     \r
156     /* We fell off the end, so we lack the configured logging type */\r
157     if (button == ctrl->radio.nbuttons) {\r
158         button=0;\r
159         cfg->logtype=LGTYP_NONE;\r
160     }\r
161     dlg_radiobutton_set(ctrl, dlg, button);\r
162     } else if (event == EVENT_VALCHANGE) {\r
163         button = dlg_radiobutton_get(ctrl, dlg);\r
164         assert(button >= 0 && button < ctrl->radio.nbuttons);\r
165         cfg->logtype = ctrl->radio.buttondata[button].i;\r
166     }\r
167 }\r
168 \r
169 static void numeric_keypad_handler(union control *ctrl, void *dlg,\r
170                                    void *data, int event)\r
171 {\r
172     int button;\r
173     Config *cfg = (Config *)data;\r
174     /*\r
175      * This function works much like the standard radio button\r
176      * handler, but it has to handle two fields in Config.\r
177      */\r
178     if (event == EVENT_REFRESH) {\r
179         if (cfg->nethack_keypad)\r
180             button = 2;\r
181         else if (cfg->app_keypad)\r
182             button = 1;\r
183         else\r
184             button = 0;\r
185         assert(button < ctrl->radio.nbuttons);\r
186         dlg_radiobutton_set(ctrl, dlg, button);\r
187     } else if (event == EVENT_VALCHANGE) {\r
188         button = dlg_radiobutton_get(ctrl, dlg);\r
189         assert(button >= 0 && button < ctrl->radio.nbuttons);\r
190         if (button == 2) {\r
191             cfg->app_keypad = FALSE;\r
192             cfg->nethack_keypad = TRUE;\r
193         } else {\r
194             cfg->app_keypad = (button != 0);\r
195             cfg->nethack_keypad = FALSE;\r
196         }\r
197     }\r
198 }\r
199 \r
200 static void cipherlist_handler(union control *ctrl, void *dlg,\r
201                                void *data, int event)\r
202 {\r
203     Config *cfg = (Config *)data;\r
204     if (event == EVENT_REFRESH) {\r
205         int i;\r
206 \r
207         static const struct { char *s; int c; } ciphers[] = {\r
208             { "3DES",                   CIPHER_3DES },\r
209             { "Blowfish",               CIPHER_BLOWFISH },\r
210             { "DES",                    CIPHER_DES },\r
211             { "AES (SSH-2 only)",       CIPHER_AES },\r
212             { "Arcfour (SSH-2 only)",   CIPHER_ARCFOUR },\r
213             { "-- warn below here --",  CIPHER_WARN }\r
214         };\r
215 \r
216         /* Set up the "selected ciphers" box. */\r
217         /* (cipherlist assumed to contain all ciphers) */\r
218         dlg_update_start(ctrl, dlg);\r
219         dlg_listbox_clear(ctrl, dlg);\r
220         for (i = 0; i < CIPHER_MAX; i++) {\r
221             int c = cfg->ssh_cipherlist[i];\r
222             int j;\r
223             char *cstr = NULL;\r
224             for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {\r
225                 if (ciphers[j].c == c) {\r
226                     cstr = ciphers[j].s;\r
227                     break;\r
228                 }\r
229             }\r
230             dlg_listbox_addwithid(ctrl, dlg, cstr, c);\r
231         }\r
232         dlg_update_done(ctrl, dlg);\r
233 \r
234     } else if (event == EVENT_VALCHANGE) {\r
235         int i;\r
236 \r
237         /* Update array to match the list box. */\r
238         for (i=0; i < CIPHER_MAX; i++)\r
239             cfg->ssh_cipherlist[i] = dlg_listbox_getid(ctrl, dlg, i);\r
240 \r
241     }\r
242 }\r
243 \r
244 #ifndef NO_GSSAPI\r
245 static void gsslist_handler(union control *ctrl, void *dlg,\r
246                             void *data, int event)\r
247 {\r
248     Config *cfg = (Config *)data;\r
249     if (event == EVENT_REFRESH) {\r
250         int i;\r
251 \r
252         dlg_update_start(ctrl, dlg);\r
253         dlg_listbox_clear(ctrl, dlg);\r
254         for (i = 0; i < ngsslibs; i++) {\r
255             int id = cfg->ssh_gsslist[i];\r
256             assert(id >= 0 && id < ngsslibs);\r
257             dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);\r
258         }\r
259         dlg_update_done(ctrl, dlg);\r
260 \r
261     } else if (event == EVENT_VALCHANGE) {\r
262         int i;\r
263 \r
264         /* Update array to match the list box. */\r
265         for (i=0; i < ngsslibs; i++)\r
266             cfg->ssh_gsslist[i] = dlg_listbox_getid(ctrl, dlg, i);\r
267     }\r
268 }\r
269 #endif\r
270 \r
271 static void kexlist_handler(union control *ctrl, void *dlg,\r
272                             void *data, int event)\r
273 {\r
274     Config *cfg = (Config *)data;\r
275     if (event == EVENT_REFRESH) {\r
276         int i;\r
277 \r
278         static const struct { char *s; int k; } kexes[] = {\r
279             { "Diffie-Hellman group 1",         KEX_DHGROUP1 },\r
280             { "Diffie-Hellman group 14",        KEX_DHGROUP14 },\r
281             { "Diffie-Hellman group exchange",  KEX_DHGEX },\r
282             { "RSA-based key exchange",         KEX_RSA },\r
283             { "-- warn below here --",          KEX_WARN }\r
284         };\r
285 \r
286         /* Set up the "kex preference" box. */\r
287         /* (kexlist assumed to contain all algorithms) */\r
288         dlg_update_start(ctrl, dlg);\r
289         dlg_listbox_clear(ctrl, dlg);\r
290         for (i = 0; i < KEX_MAX; i++) {\r
291             int k = cfg->ssh_kexlist[i];\r
292             int j;\r
293             char *kstr = NULL;\r
294             for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {\r
295                 if (kexes[j].k == k) {\r
296                     kstr = kexes[j].s;\r
297                     break;\r
298                 }\r
299             }\r
300             dlg_listbox_addwithid(ctrl, dlg, kstr, k);\r
301         }\r
302         dlg_update_done(ctrl, dlg);\r
303 \r
304     } else if (event == EVENT_VALCHANGE) {\r
305         int i;\r
306 \r
307         /* Update array to match the list box. */\r
308         for (i=0; i < KEX_MAX; i++)\r
309             cfg->ssh_kexlist[i] = dlg_listbox_getid(ctrl, dlg, i);\r
310 \r
311     }\r
312 }\r
313 \r
314 static void printerbox_handler(union control *ctrl, void *dlg,\r
315                                void *data, int event)\r
316 {\r
317     Config *cfg = (Config *)data;\r
318     if (event == EVENT_REFRESH) {\r
319         int nprinters, i;\r
320         printer_enum *pe;\r
321 \r
322         dlg_update_start(ctrl, dlg);\r
323         /*\r
324          * Some backends may wish to disable the drop-down list on\r
325          * this edit box. Be prepared for this.\r
326          */\r
327         if (ctrl->editbox.has_list) {\r
328             dlg_listbox_clear(ctrl, dlg);\r
329             dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);\r
330             pe = printer_start_enum(&nprinters);\r
331             for (i = 0; i < nprinters; i++)\r
332                 dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));\r
333             printer_finish_enum(pe);\r
334         }\r
335         dlg_editbox_set(ctrl, dlg,\r
336                         (*cfg->printer ? cfg->printer :\r
337                          PRINTER_DISABLED_STRING));\r
338         dlg_update_done(ctrl, dlg);\r
339     } else if (event == EVENT_VALCHANGE) {\r
340         dlg_editbox_get(ctrl, dlg, cfg->printer, sizeof(cfg->printer));\r
341         if (!strcmp(cfg->printer, PRINTER_DISABLED_STRING))\r
342             *cfg->printer = '\0';\r
343     }\r
344 }\r
345 \r
346 static void codepage_handler(union control *ctrl, void *dlg,\r
347                              void *data, int event)\r
348 {\r
349     Config *cfg = (Config *)data;\r
350     if (event == EVENT_REFRESH) {\r
351         int i;\r
352         const char *cp, *thiscp;\r
353         dlg_update_start(ctrl, dlg);\r
354         thiscp = cp_name(decode_codepage(cfg->line_codepage));\r
355         dlg_listbox_clear(ctrl, dlg);\r
356         for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)\r
357             dlg_listbox_add(ctrl, dlg, cp);\r
358         dlg_editbox_set(ctrl, dlg, thiscp);\r
359         strcpy(cfg->line_codepage, thiscp);\r
360         dlg_update_done(ctrl, dlg);\r
361     } else if (event == EVENT_VALCHANGE) {\r
362         dlg_editbox_get(ctrl, dlg, cfg->line_codepage,\r
363                         sizeof(cfg->line_codepage));\r
364         strcpy(cfg->line_codepage,\r
365                cp_name(decode_codepage(cfg->line_codepage)));\r
366     }\r
367 }\r
368 \r
369 static void sshbug_handler(union control *ctrl, void *dlg,\r
370                            void *data, int event)\r
371 {\r
372     if (event == EVENT_REFRESH) {\r
373         dlg_update_start(ctrl, dlg);\r
374         dlg_listbox_clear(ctrl, dlg);\r
375         dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);\r
376         dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);\r
377         dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);\r
378         switch (*(int *)ATOFFSET(data, ctrl->listbox.context.i)) {\r
379           case AUTO:      dlg_listbox_select(ctrl, dlg, 0); break;\r
380           case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;\r
381           case FORCE_ON:  dlg_listbox_select(ctrl, dlg, 2); break;\r
382         }\r
383         dlg_update_done(ctrl, dlg);\r
384     } else if (event == EVENT_SELCHANGE) {\r
385         int i = dlg_listbox_index(ctrl, dlg);\r
386         if (i < 0)\r
387             i = AUTO;\r
388         else\r
389             i = dlg_listbox_getid(ctrl, dlg, i);\r
390         *(int *)ATOFFSET(data, ctrl->listbox.context.i) = i;\r
391     }\r
392 }\r
393 \r
394 #define SAVEDSESSION_LEN 2048\r
395 \r
396 struct sessionsaver_data {\r
397     union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;\r
398     union control *okbutton, *cancelbutton;\r
399     struct sesslist sesslist;\r
400     int midsession;\r
401 };\r
402 \r
403 /* \r
404  * Helper function to load the session selected in the list box, if\r
405  * any, as this is done in more than one place below. Returns 0 for\r
406  * failure.\r
407  */\r
408 static int load_selected_session(struct sessionsaver_data *ssd,\r
409                                  char *savedsession,\r
410                                  void *dlg, Config *cfg, int *maybe_launch)\r
411 {\r
412     int i = dlg_listbox_index(ssd->listbox, dlg);\r
413     int isdef;\r
414     if (i < 0) {\r
415         dlg_beep(dlg);\r
416         return 0;\r
417     }\r
418     isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");\r
419     load_settings(ssd->sesslist.sessions[i], cfg);\r
420     if (!isdef) {\r
421         strncpy(savedsession, ssd->sesslist.sessions[i],\r
422                 SAVEDSESSION_LEN);\r
423         savedsession[SAVEDSESSION_LEN-1] = '\0';\r
424         if (maybe_launch)\r
425             *maybe_launch = TRUE;\r
426     } else {\r
427         savedsession[0] = '\0';\r
428         if (maybe_launch)\r
429             *maybe_launch = FALSE;\r
430     }\r
431     dlg_refresh(NULL, dlg);\r
432     /* Restore the selection, which might have been clobbered by\r
433      * changing the value of the edit box. */\r
434     dlg_listbox_select(ssd->listbox, dlg, i);\r
435     return 1;\r
436 }\r
437 \r
438 static void sessionsaver_handler(union control *ctrl, void *dlg,\r
439                                  void *data, int event)\r
440 {\r
441     Config *cfg = (Config *)data;\r
442     struct sessionsaver_data *ssd =\r
443         (struct sessionsaver_data *)ctrl->generic.context.p;\r
444     char *savedsession;\r
445 \r
446     /*\r
447      * The first time we're called in a new dialog, we must\r
448      * allocate space to store the current contents of the saved\r
449      * session edit box (since it must persist even when we switch\r
450      * panels, but is not part of the Config).\r
451      */\r
452     if (!ssd->editbox) {\r
453         savedsession = NULL;\r
454     } else if (!dlg_get_privdata(ssd->editbox, dlg)) {\r
455         savedsession = (char *)\r
456             dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN);\r
457         savedsession[0] = '\0';\r
458     } else {\r
459         savedsession = dlg_get_privdata(ssd->editbox, dlg);\r
460     }\r
461 \r
462     if (event == EVENT_REFRESH) {\r
463         if (ctrl == ssd->editbox) {\r
464             dlg_editbox_set(ctrl, dlg, savedsession);\r
465         } else if (ctrl == ssd->listbox) {\r
466             int i;\r
467             dlg_update_start(ctrl, dlg);\r
468             dlg_listbox_clear(ctrl, dlg);\r
469             for (i = 0; i < ssd->sesslist.nsessions; i++)\r
470                 dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);\r
471             dlg_update_done(ctrl, dlg);\r
472         }\r
473     } else if (event == EVENT_VALCHANGE) {\r
474         int top, bottom, halfway, i;\r
475         if (ctrl == ssd->editbox) {\r
476             dlg_editbox_get(ctrl, dlg, savedsession,\r
477                             SAVEDSESSION_LEN);\r
478             top = ssd->sesslist.nsessions;\r
479             bottom = -1;\r
480             while (top-bottom > 1) {\r
481                 halfway = (top+bottom)/2;\r
482                 i = strcmp(savedsession, ssd->sesslist.sessions[halfway]);\r
483                 if (i <= 0 ) {\r
484                     top = halfway;\r
485                 } else {\r
486                     bottom = halfway;\r
487                 }\r
488             }\r
489             if (top == ssd->sesslist.nsessions) {\r
490                 top -= 1;\r
491             }\r
492             dlg_listbox_select(ssd->listbox, dlg, top);\r
493         }\r
494     } else if (event == EVENT_ACTION) {\r
495         int mbl = FALSE;\r
496         if (!ssd->midsession &&\r
497             (ctrl == ssd->listbox ||\r
498              (ssd->loadbutton && ctrl == ssd->loadbutton))) {\r
499             /*\r
500              * The user has double-clicked a session, or hit Load.\r
501              * We must load the selected session, and then\r
502              * terminate the configuration dialog _if_ there was a\r
503              * double-click on the list box _and_ that session\r
504              * contains a hostname.\r
505              */\r
506             if (load_selected_session(ssd, savedsession, dlg, cfg, &mbl) &&\r
507                 (mbl && ctrl == ssd->listbox && cfg_launchable(cfg))) {\r
508                 dlg_end(dlg, 1);       /* it's all over, and succeeded */\r
509             }\r
510         } else if (ctrl == ssd->savebutton) {\r
511             int isdef = !strcmp(savedsession, "Default Settings");\r
512             if (!savedsession[0]) {\r
513                 int i = dlg_listbox_index(ssd->listbox, dlg);\r
514                 if (i < 0) {\r
515                     dlg_beep(dlg);\r
516                     return;\r
517                 }\r
518                 isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");\r
519                 if (!isdef) {\r
520                     strncpy(savedsession, ssd->sesslist.sessions[i],\r
521                             SAVEDSESSION_LEN);\r
522                     savedsession[SAVEDSESSION_LEN-1] = '\0';\r
523                 } else {\r
524                     savedsession[0] = '\0';\r
525                 }\r
526             }\r
527             {\r
528                 char *errmsg = save_settings(savedsession, cfg);\r
529                 if (errmsg) {\r
530                     dlg_error_msg(dlg, errmsg);\r
531                     sfree(errmsg);\r
532                 }\r
533             }\r
534             get_sesslist(&ssd->sesslist, FALSE);\r
535             get_sesslist(&ssd->sesslist, TRUE);\r
536             dlg_refresh(ssd->editbox, dlg);\r
537             dlg_refresh(ssd->listbox, dlg);\r
538         } else if (!ssd->midsession &&\r
539                    ssd->delbutton && ctrl == ssd->delbutton) {\r
540             int i = dlg_listbox_index(ssd->listbox, dlg);\r
541             if (i <= 0) {\r
542                 dlg_beep(dlg);\r
543             } else {\r
544                 del_settings(ssd->sesslist.sessions[i]);\r
545                 get_sesslist(&ssd->sesslist, FALSE);\r
546                 get_sesslist(&ssd->sesslist, TRUE);\r
547                 dlg_refresh(ssd->listbox, dlg);\r
548             }\r
549         } else if (ctrl == ssd->okbutton) {\r
550             if (ssd->midsession) {\r
551                 /* In a mid-session Change Settings, Apply is always OK. */\r
552                 dlg_end(dlg, 1);\r
553                 return;\r
554             }\r
555             /*\r
556              * Annoying special case. If the `Open' button is\r
557              * pressed while no host name is currently set, _and_\r
558              * the session list previously had the focus, _and_\r
559              * there was a session selected in that which had a\r
560              * valid host name in it, then load it and go.\r
561              */\r
562             if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&\r
563                 !cfg_launchable(cfg)) {\r
564                 Config cfg2;\r
565                 int mbl = FALSE;\r
566                 if (!load_selected_session(ssd, savedsession, dlg,\r
567                                            &cfg2, &mbl)) {\r
568                     dlg_beep(dlg);\r
569                     return;\r
570                 }\r
571                 /* If at this point we have a valid session, go! */\r
572                 if (mbl && cfg_launchable(&cfg2)) {\r
573                     *cfg = cfg2;       /* structure copy */\r
574                     cfg->remote_cmd_ptr = NULL;\r
575                     dlg_end(dlg, 1);\r
576                 } else\r
577                     dlg_beep(dlg);\r
578                 return;\r
579             }\r
580 \r
581             /*\r
582              * Otherwise, do the normal thing: if we have a valid\r
583              * session, get going.\r
584              */\r
585             if (cfg_launchable(cfg)) {\r
586                 dlg_end(dlg, 1);\r
587             } else\r
588                 dlg_beep(dlg);\r
589         } else if (ctrl == ssd->cancelbutton) {\r
590             dlg_end(dlg, 0);\r
591         }\r
592     }\r
593 }\r
594 \r
595 struct charclass_data {\r
596     union control *listbox, *editbox, *button;\r
597 };\r
598 \r
599 static void charclass_handler(union control *ctrl, void *dlg,\r
600                               void *data, int event)\r
601 {\r
602     Config *cfg = (Config *)data;\r
603     struct charclass_data *ccd =\r
604         (struct charclass_data *)ctrl->generic.context.p;\r
605 \r
606     if (event == EVENT_REFRESH) {\r
607         if (ctrl == ccd->listbox) {\r
608             int i;\r
609             dlg_update_start(ctrl, dlg);\r
610             dlg_listbox_clear(ctrl, dlg);\r
611             for (i = 0; i < 128; i++) {\r
612                 char str[100];\r
613                 sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,\r
614                         (i >= 0x21 && i != 0x7F) ? i : ' ', cfg->wordness[i]);\r
615                 dlg_listbox_add(ctrl, dlg, str);\r
616             }\r
617             dlg_update_done(ctrl, dlg);\r
618         }\r
619     } else if (event == EVENT_ACTION) {\r
620         if (ctrl == ccd->button) {\r
621             char str[100];\r
622             int i, n;\r
623             dlg_editbox_get(ccd->editbox, dlg, str, sizeof(str));\r
624             n = atoi(str);\r
625             for (i = 0; i < 128; i++) {\r
626                 if (dlg_listbox_issel(ccd->listbox, dlg, i))\r
627                     cfg->wordness[i] = n;\r
628             }\r
629             dlg_refresh(ccd->listbox, dlg);\r
630         }\r
631     }\r
632 }\r
633 \r
634 struct colour_data {\r
635     union control *listbox, *redit, *gedit, *bedit, *button;\r
636 };\r
637 \r
638 static const char *const colours[] = {\r
639     "Default Foreground", "Default Bold Foreground",\r
640     "Default Background", "Default Bold Background",\r
641     "Cursor Text", "Cursor Colour",\r
642     "ANSI Black", "ANSI Black Bold",\r
643     "ANSI Red", "ANSI Red Bold",\r
644     "ANSI Green", "ANSI Green Bold",\r
645     "ANSI Yellow", "ANSI Yellow Bold",\r
646     "ANSI Blue", "ANSI Blue Bold",\r
647     "ANSI Magenta", "ANSI Magenta Bold",\r
648     "ANSI Cyan", "ANSI Cyan Bold",\r
649     "ANSI White", "ANSI White Bold"\r
650 };\r
651 \r
652 static void colour_handler(union control *ctrl, void *dlg,\r
653                             void *data, int event)\r
654 {\r
655     Config *cfg = (Config *)data;\r
656     struct colour_data *cd =\r
657         (struct colour_data *)ctrl->generic.context.p;\r
658     int update = FALSE, clear = FALSE, r, g, b;\r
659 \r
660     if (event == EVENT_REFRESH) {\r
661         if (ctrl == cd->listbox) {\r
662             int i;\r
663             dlg_update_start(ctrl, dlg);\r
664             dlg_listbox_clear(ctrl, dlg);\r
665             for (i = 0; i < lenof(colours); i++)\r
666                 dlg_listbox_add(ctrl, dlg, colours[i]);\r
667             dlg_update_done(ctrl, dlg);\r
668             clear = TRUE;\r
669             update = TRUE;\r
670         }\r
671     } else if (event == EVENT_SELCHANGE) {\r
672         if (ctrl == cd->listbox) {\r
673             /* The user has selected a colour. Update the RGB text. */\r
674             int i = dlg_listbox_index(ctrl, dlg);\r
675             if (i < 0) {\r
676                 clear = TRUE;\r
677             } else {\r
678                 clear = FALSE;\r
679                 r = cfg->colours[i][0];\r
680                 g = cfg->colours[i][1];\r
681                 b = cfg->colours[i][2];\r
682             }\r
683             update = TRUE;\r
684         }\r
685     } else if (event == EVENT_VALCHANGE) {\r
686         if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {\r
687             /* The user has changed the colour using the edit boxes. */\r
688             char buf[80];\r
689             int i, cval;\r
690 \r
691             dlg_editbox_get(ctrl, dlg, buf, lenof(buf));\r
692             cval = atoi(buf);\r
693             if (cval > 255) cval = 255;\r
694             if (cval < 0)   cval = 0;\r
695 \r
696             i = dlg_listbox_index(cd->listbox, dlg);\r
697             if (i >= 0) {\r
698                 if (ctrl == cd->redit)\r
699                     cfg->colours[i][0] = cval;\r
700                 else if (ctrl == cd->gedit)\r
701                     cfg->colours[i][1] = cval;\r
702                 else if (ctrl == cd->bedit)\r
703                     cfg->colours[i][2] = cval;\r
704             }\r
705         }\r
706     } else if (event == EVENT_ACTION) {\r
707         if (ctrl == cd->button) {\r
708             int i = dlg_listbox_index(cd->listbox, dlg);\r
709             if (i < 0) {\r
710                 dlg_beep(dlg);\r
711                 return;\r
712             }\r
713             /*\r
714              * Start a colour selector, which will send us an\r
715              * EVENT_CALLBACK when it's finished and allow us to\r
716              * pick up the results.\r
717              */\r
718             dlg_coloursel_start(ctrl, dlg,\r
719                                 cfg->colours[i][0],\r
720                                 cfg->colours[i][1],\r
721                                 cfg->colours[i][2]);\r
722         }\r
723     } else if (event == EVENT_CALLBACK) {\r
724         if (ctrl == cd->button) {\r
725             int i = dlg_listbox_index(cd->listbox, dlg);\r
726             /*\r
727              * Collect the results of the colour selector. Will\r
728              * return nonzero on success, or zero if the colour\r
729              * selector did nothing (user hit Cancel, for example).\r
730              */\r
731             if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {\r
732                 cfg->colours[i][0] = r;\r
733                 cfg->colours[i][1] = g;\r
734                 cfg->colours[i][2] = b;\r
735                 clear = FALSE;\r
736                 update = TRUE;\r
737             }\r
738         }\r
739     }\r
740 \r
741     if (update) {\r
742         if (clear) {\r
743             dlg_editbox_set(cd->redit, dlg, "");\r
744             dlg_editbox_set(cd->gedit, dlg, "");\r
745             dlg_editbox_set(cd->bedit, dlg, "");\r
746         } else {\r
747             char buf[40];\r
748             sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);\r
749             sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);\r
750             sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);\r
751         }\r
752     }\r
753 }\r
754 \r
755 struct ttymodes_data {\r
756     union control *modelist, *valradio, *valbox;\r
757     union control *addbutton, *rembutton, *listbox;\r
758 };\r
759 \r
760 static void ttymodes_handler(union control *ctrl, void *dlg,\r
761                              void *data, int event)\r
762 {\r
763     Config *cfg = (Config *)data;\r
764     struct ttymodes_data *td =\r
765         (struct ttymodes_data *)ctrl->generic.context.p;\r
766 \r
767     if (event == EVENT_REFRESH) {\r
768         if (ctrl == td->listbox) {\r
769             char *p = cfg->ttymodes;\r
770             dlg_update_start(ctrl, dlg);\r
771             dlg_listbox_clear(ctrl, dlg);\r
772             while (*p) {\r
773                 int tabpos = strchr(p, '\t') - p;\r
774                 char *disp = dupprintf("%.*s\t%s", tabpos, p,\r
775                                        (p[tabpos+1] == 'A') ? "(auto)" :\r
776                                        p+tabpos+2);\r
777                 dlg_listbox_add(ctrl, dlg, disp);\r
778                 p += strlen(p) + 1;\r
779                 sfree(disp);\r
780             }\r
781             dlg_update_done(ctrl, dlg);\r
782         } else if (ctrl == td->modelist) {\r
783             int i;\r
784             dlg_update_start(ctrl, dlg);\r
785             dlg_listbox_clear(ctrl, dlg);\r
786             for (i = 0; ttymodes[i]; i++)\r
787                 dlg_listbox_add(ctrl, dlg, ttymodes[i]);\r
788             dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */\r
789             dlg_update_done(ctrl, dlg);\r
790         } else if (ctrl == td->valradio) {\r
791             dlg_radiobutton_set(ctrl, dlg, 0);\r
792         }\r
793     } else if (event == EVENT_ACTION) {\r
794         if (ctrl == td->addbutton) {\r
795             int ind = dlg_listbox_index(td->modelist, dlg);\r
796             if (ind >= 0) {\r
797                 char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';\r
798                 int slen, left;\r
799                 char *p, str[lenof(cfg->ttymodes)];\r
800                 /* Construct new entry */\r
801                 memset(str, 0, lenof(str));\r
802                 strncpy(str, ttymodes[ind], lenof(str)-3);\r
803                 slen = strlen(str);\r
804                 str[slen] = '\t';\r
805                 str[slen+1] = type;\r
806                 slen += 2;\r
807                 if (type == 'V') {\r
808                     dlg_editbox_get(td->valbox, dlg, str+slen, lenof(str)-slen);\r
809                 }\r
810                 /* Find end of list, deleting any existing instance */\r
811                 p = cfg->ttymodes;\r
812                 left = lenof(cfg->ttymodes);\r
813                 while (*p) {\r
814                     int t = strchr(p, '\t') - p;\r
815                     if (t == strlen(ttymodes[ind]) &&\r
816                         strncmp(p, ttymodes[ind], t) == 0) {\r
817                         memmove(p, p+strlen(p)+1, left - (strlen(p)+1));\r
818                         continue;\r
819                     }\r
820                     left -= strlen(p) + 1;\r
821                     p    += strlen(p) + 1;\r
822                 }\r
823                 /* Append new entry */\r
824                 memset(p, 0, left);\r
825                 strncpy(p, str, left - 2);\r
826                 dlg_refresh(td->listbox, dlg);\r
827             } else\r
828                 dlg_beep(dlg);\r
829         } else if (ctrl == td->rembutton) {\r
830             char *p = cfg->ttymodes;\r
831             int i = 0, len = lenof(cfg->ttymodes);\r
832             while (*p) {\r
833                 int multisel = dlg_listbox_index(td->listbox, dlg) < 0;\r
834                 if (dlg_listbox_issel(td->listbox, dlg, i)) {\r
835                     if (!multisel) {\r
836                         /* Populate controls with entry we're about to\r
837                          * delete, for ease of editing.\r
838                          * (If multiple entries were selected, don't\r
839                          * touch the controls.) */\r
840                         char *val = strchr(p, '\t');\r
841                         if (val) {\r
842                             int ind = 0;\r
843                             val++;\r
844                             while (ttymodes[ind]) {\r
845                                 if (strlen(ttymodes[ind]) == val-p-1 &&\r
846                                     !strncmp(ttymodes[ind], p, val-p-1))\r
847                                     break;\r
848                                 ind++;\r
849                             }\r
850                             dlg_listbox_select(td->modelist, dlg, ind);\r
851                             dlg_radiobutton_set(td->valradio, dlg,\r
852                                                 (*val == 'V'));\r
853                             dlg_editbox_set(td->valbox, dlg, val+1);\r
854                         }\r
855                     }\r
856                     memmove(p, p+strlen(p)+1, len - (strlen(p)+1));\r
857                     i++;\r
858                     continue;\r
859                 }\r
860                 len -= strlen(p) + 1;\r
861                 p   += strlen(p) + 1;\r
862                 i++;\r
863             }\r
864             memset(p, 0, lenof(cfg->ttymodes) - len);\r
865             dlg_refresh(td->listbox, dlg);\r
866         }\r
867     }\r
868 }\r
869 \r
870 struct environ_data {\r
871     union control *varbox, *valbox, *addbutton, *rembutton, *listbox;\r
872 };\r
873 \r
874 static void environ_handler(union control *ctrl, void *dlg,\r
875                             void *data, int event)\r
876 {\r
877     Config *cfg = (Config *)data;\r
878     struct environ_data *ed =\r
879         (struct environ_data *)ctrl->generic.context.p;\r
880 \r
881     if (event == EVENT_REFRESH) {\r
882         if (ctrl == ed->listbox) {\r
883             char *p = cfg->environmt;\r
884             dlg_update_start(ctrl, dlg);\r
885             dlg_listbox_clear(ctrl, dlg);\r
886             while (*p) {\r
887                 dlg_listbox_add(ctrl, dlg, p);\r
888                 p += strlen(p) + 1;\r
889             }\r
890             dlg_update_done(ctrl, dlg);\r
891         }\r
892     } else if (event == EVENT_ACTION) {\r
893         if (ctrl == ed->addbutton) {\r
894             char str[sizeof(cfg->environmt)];\r
895             char *p;\r
896             dlg_editbox_get(ed->varbox, dlg, str, sizeof(str)-1);\r
897             if (!*str) {\r
898                 dlg_beep(dlg);\r
899                 return;\r
900             }\r
901             p = str + strlen(str);\r
902             *p++ = '\t';\r
903             dlg_editbox_get(ed->valbox, dlg, p, sizeof(str)-1 - (p - str));\r
904             if (!*p) {\r
905                 dlg_beep(dlg);\r
906                 return;\r
907             }\r
908             p = cfg->environmt;\r
909             while (*p) {\r
910                 while (*p)\r
911                     p++;\r
912                 p++;\r
913             }\r
914             if ((p - cfg->environmt) + strlen(str) + 2 <\r
915                 sizeof(cfg->environmt)) {\r
916                 strcpy(p, str);\r
917                 p[strlen(str) + 1] = '\0';\r
918                 dlg_listbox_add(ed->listbox, dlg, str);\r
919                 dlg_editbox_set(ed->varbox, dlg, "");\r
920                 dlg_editbox_set(ed->valbox, dlg, "");\r
921             } else {\r
922                 dlg_error_msg(dlg, "Environment too big");\r
923             }\r
924         } else if (ctrl == ed->rembutton) {\r
925             int i = dlg_listbox_index(ed->listbox, dlg);\r
926             if (i < 0) {\r
927                 dlg_beep(dlg);\r
928             } else {\r
929                 char *p, *q, *str;\r
930 \r
931                 dlg_listbox_del(ed->listbox, dlg, i);\r
932                 p = cfg->environmt;\r
933                 while (i > 0) {\r
934                     if (!*p)\r
935                         goto disaster;\r
936                     while (*p)\r
937                         p++;\r
938                     p++;\r
939                     i--;\r
940                 }\r
941                 q = p;\r
942                 if (!*p)\r
943                     goto disaster;\r
944                 /* Populate controls with the entry we're about to delete\r
945                  * for ease of editing */\r
946                 str = p;\r
947                 p = strchr(p, '\t');\r
948                 if (!p)\r
949                     goto disaster;\r
950                 *p = '\0';\r
951                 dlg_editbox_set(ed->varbox, dlg, str);\r
952                 p++;\r
953                 str = p;\r
954                 dlg_editbox_set(ed->valbox, dlg, str);\r
955                 p = strchr(p, '\0');\r
956                 if (!p)\r
957                     goto disaster;\r
958                 p++;\r
959                 while (*p) {\r
960                     while (*p)\r
961                         *q++ = *p++;\r
962                     *q++ = *p++;\r
963                 }\r
964                 *q = '\0';\r
965                 disaster:;\r
966             }\r
967         }\r
968     }\r
969 }\r
970 \r
971 struct portfwd_data {\r
972     union control *addbutton, *rembutton, *listbox;\r
973     union control *sourcebox, *destbox, *direction;\r
974 #ifndef NO_IPV6\r
975     union control *addressfamily;\r
976 #endif\r
977 };\r
978 \r
979 static void portfwd_handler(union control *ctrl, void *dlg,\r
980                             void *data, int event)\r
981 {\r
982     Config *cfg = (Config *)data;\r
983     struct portfwd_data *pfd =\r
984         (struct portfwd_data *)ctrl->generic.context.p;\r
985 \r
986     if (event == EVENT_REFRESH) {\r
987         if (ctrl == pfd->listbox) {\r
988             char *p = cfg->portfwd;\r
989             dlg_update_start(ctrl, dlg);\r
990             dlg_listbox_clear(ctrl, dlg);\r
991             while (*p) {\r
992                 dlg_listbox_add(ctrl, dlg, p);\r
993                 p += strlen(p) + 1;\r
994             }\r
995             dlg_update_done(ctrl, dlg);\r
996         } else if (ctrl == pfd->direction) {\r
997             /*\r
998              * Default is Local.\r
999              */\r
1000             dlg_radiobutton_set(ctrl, dlg, 0);\r
1001 #ifndef NO_IPV6\r
1002         } else if (ctrl == pfd->addressfamily) {\r
1003             dlg_radiobutton_set(ctrl, dlg, 0);\r
1004 #endif\r
1005         }\r
1006     } else if (event == EVENT_ACTION) {\r
1007         if (ctrl == pfd->addbutton) {\r
1008             char str[sizeof(cfg->portfwd)];\r
1009             char *p;\r
1010             int i, type;\r
1011             int whichbutton;\r
1012 \r
1013             i = 0;\r
1014 #ifndef NO_IPV6\r
1015             whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);\r
1016             if (whichbutton == 1)\r
1017                 str[i++] = '4';\r
1018             else if (whichbutton == 2)\r
1019                 str[i++] = '6';\r
1020 #endif\r
1021 \r
1022             whichbutton = dlg_radiobutton_get(pfd->direction, dlg);\r
1023             if (whichbutton == 0)\r
1024                 type = 'L';\r
1025             else if (whichbutton == 1)\r
1026                 type = 'R';\r
1027             else\r
1028                 type = 'D';\r
1029             str[i++] = type;\r
1030 \r
1031             dlg_editbox_get(pfd->sourcebox, dlg, str+i, sizeof(str) - i);\r
1032             if (!str[i]) {\r
1033                 dlg_error_msg(dlg, "You need to specify a source port number");\r
1034                 return;\r
1035             }\r
1036             p = str + strlen(str);\r
1037             if (type != 'D') {\r
1038                 *p++ = '\t';\r
1039                 dlg_editbox_get(pfd->destbox, dlg, p,\r
1040                                 sizeof(str) - (p - str));\r
1041                 if (!*p || !strchr(p, ':')) {\r
1042                     dlg_error_msg(dlg,\r
1043                                   "You need to specify a destination address\n"\r
1044                                   "in the form \"host.name:port\"");\r
1045                     return;\r
1046                 }\r
1047             } else\r
1048                 *p = '\0';\r
1049             p = cfg->portfwd;\r
1050             while (*p) {\r
1051                 if (strcmp(p,str) == 0) {\r
1052                     dlg_error_msg(dlg, "Specified forwarding already exists");\r
1053                     break;\r
1054                 }\r
1055                 while (*p)\r
1056                     p++;\r
1057                 p++;\r
1058             }\r
1059             if (!*p) {\r
1060                 if ((p - cfg->portfwd) + strlen(str) + 2 <=\r
1061                     sizeof(cfg->portfwd)) {\r
1062                     strcpy(p, str);\r
1063                     p[strlen(str) + 1] = '\0';\r
1064                     dlg_listbox_add(pfd->listbox, dlg, str);\r
1065                     dlg_editbox_set(pfd->sourcebox, dlg, "");\r
1066                     dlg_editbox_set(pfd->destbox, dlg, "");\r
1067                 } else {\r
1068                     dlg_error_msg(dlg, "Too many forwardings");\r
1069                 }\r
1070             }\r
1071         } else if (ctrl == pfd->rembutton) {\r
1072             int i = dlg_listbox_index(pfd->listbox, dlg);\r
1073             if (i < 0)\r
1074                 dlg_beep(dlg);\r
1075             else {\r
1076                 char *p, *q, *src, *dst;\r
1077                 char dir;\r
1078 \r
1079                 dlg_listbox_del(pfd->listbox, dlg, i);\r
1080                 p = cfg->portfwd;\r
1081                 while (i > 0) {\r
1082                     if (!*p)\r
1083                         goto disaster2;\r
1084                     while (*p)\r
1085                         p++;\r
1086                     p++;\r
1087                     i--;\r
1088                 }\r
1089                 q = p;\r
1090                 if (!*p)\r
1091                     goto disaster2;\r
1092                 /* Populate the controls with the entry we're about to\r
1093                  * delete, for ease of editing. */\r
1094                 {\r
1095                     static const char *const afs = "A46";\r
1096                     char *afp = strchr(afs, *p);\r
1097 #ifndef NO_IPV6\r
1098                     int idx = afp ? afp-afs : 0;\r
1099 #endif\r
1100                     if (afp)\r
1101                         p++;\r
1102 #ifndef NO_IPV6\r
1103                     dlg_radiobutton_set(pfd->addressfamily, dlg, idx);\r
1104 #endif\r
1105                 }\r
1106                 {\r
1107                     static const char *const dirs = "LRD";\r
1108                     dir = *p;\r
1109                     dlg_radiobutton_set(pfd->direction, dlg,\r
1110                                         strchr(dirs, dir) - dirs);\r
1111                 }\r
1112                 p++;\r
1113                 if (dir != 'D') {\r
1114                     src = p;\r
1115                     p = strchr(p, '\t');\r
1116                     if (!p)\r
1117                         goto disaster2;\r
1118                     *p = '\0';\r
1119                     p++;\r
1120                     dst = p;\r
1121                 } else {\r
1122                     src = p;\r
1123                     dst = "";\r
1124                 }\r
1125                 p = strchr(p, '\0');\r
1126                 if (!p)\r
1127                     goto disaster2;\r
1128                 dlg_editbox_set(pfd->sourcebox, dlg, src);\r
1129                 dlg_editbox_set(pfd->destbox, dlg, dst);\r
1130                 p++;\r
1131                 while (*p) {\r
1132                     while (*p)\r
1133                         *q++ = *p++;\r
1134                     *q++ = *p++;\r
1135                 }\r
1136                 *q = '\0';\r
1137                 disaster2:;\r
1138             }\r
1139         }\r
1140     }\r
1141 }\r
1142 \r
1143 void setup_config_box(struct controlbox *b, int midsession,\r
1144                       int protocol, int protcfginfo)\r
1145 {\r
1146     struct controlset *s;\r
1147     struct sessionsaver_data *ssd;\r
1148     struct charclass_data *ccd;\r
1149     struct colour_data *cd;\r
1150     struct ttymodes_data *td;\r
1151     struct environ_data *ed;\r
1152     struct portfwd_data *pfd;\r
1153     union control *c;\r
1154     char *str;\r
1155 \r
1156     ssd = (struct sessionsaver_data *)\r
1157         ctrl_alloc(b, sizeof(struct sessionsaver_data));\r
1158     memset(ssd, 0, sizeof(*ssd));\r
1159     ssd->midsession = midsession;\r
1160 \r
1161     /*\r
1162      * The standard panel that appears at the bottom of all panels:\r
1163      * Open, Cancel, Apply etc.\r
1164      */\r
1165     s = ctrl_getset(b, "", "", "");\r
1166     ctrl_columns(s, 5, 20, 20, 20, 20, 20);\r
1167     ssd->okbutton = ctrl_pushbutton(s,\r
1168                                     (midsession ? "Apply" : "Open"),\r
1169                                     (char)(midsession ? 'a' : 'o'),\r
1170                                     HELPCTX(no_help),\r
1171                                     sessionsaver_handler, P(ssd));\r
1172     ssd->okbutton->button.isdefault = TRUE;\r
1173     ssd->okbutton->generic.column = 3;\r
1174     ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),\r
1175                                         sessionsaver_handler, P(ssd));\r
1176     ssd->cancelbutton->button.iscancel = TRUE;\r
1177     ssd->cancelbutton->generic.column = 4;\r
1178     /* We carefully don't close the 5-column part, so that platform-\r
1179      * specific add-ons can put extra buttons alongside Open and Cancel. */\r
1180 \r
1181     /*\r
1182      * The Session panel.\r
1183      */\r
1184     str = dupprintf("Basic options for your %s session", appname);\r
1185     ctrl_settitle(b, "Session", str);\r
1186     sfree(str);\r
1187 \r
1188     if (!midsession) {\r
1189         struct hostport *hp = (struct hostport *)\r
1190             ctrl_alloc(b, sizeof(struct hostport));\r
1191 \r
1192         s = ctrl_getset(b, "Session", "hostport",\r
1193                         "Specify the destination you want to connect to");\r
1194         ctrl_columns(s, 2, 75, 25);\r
1195         c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,\r
1196                          HELPCTX(session_hostname),\r
1197                          config_host_handler, I(0), I(0));\r
1198         c->generic.column = 0;\r
1199         hp->host = c;\r
1200         c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,\r
1201                          HELPCTX(session_hostname),\r
1202                          config_port_handler, I(0), I(0));\r
1203         c->generic.column = 1;\r
1204         hp->port = c;\r
1205         ctrl_columns(s, 1, 100);\r
1206 \r
1207         if (!backend_from_proto(PROT_SSH)) {\r
1208             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3,\r
1209                               HELPCTX(session_hostname),\r
1210                               config_protocolbuttons_handler, P(hp),\r
1211                               "Raw", 'w', I(PROT_RAW),\r
1212                               "Telnet", 't', I(PROT_TELNET),\r
1213                               "Rlogin", 'i', I(PROT_RLOGIN),\r
1214                               NULL);\r
1215         } else {\r
1216             ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4,\r
1217                               HELPCTX(session_hostname),\r
1218                               config_protocolbuttons_handler, P(hp),\r
1219                               "Raw", 'w', I(PROT_RAW),\r
1220                               "Telnet", 't', I(PROT_TELNET),\r
1221                               "Rlogin", 'i', I(PROT_RLOGIN),\r
1222                               "SSH", 's', I(PROT_SSH),\r
1223                               NULL);\r
1224         }\r
1225     }\r
1226 \r
1227     /*\r
1228      * The Load/Save panel is available even in mid-session.\r
1229      */\r
1230     s = ctrl_getset(b, "Session", "savedsessions",\r
1231                     midsession ? "Save the current session settings" :\r
1232                     "Load, save or delete a stored session");\r
1233     ctrl_columns(s, 2, 75, 25);\r
1234     get_sesslist(&ssd->sesslist, TRUE);\r
1235     ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,\r
1236                                 HELPCTX(session_saved),\r
1237                                 sessionsaver_handler, P(ssd), P(NULL));\r
1238     ssd->editbox->generic.column = 0;\r
1239     /* Reset columns so that the buttons are alongside the list, rather\r
1240      * than alongside that edit box. */\r
1241     ctrl_columns(s, 1, 100);\r
1242     ctrl_columns(s, 2, 75, 25);\r
1243     ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
1244                                 HELPCTX(session_saved),\r
1245                                 sessionsaver_handler, P(ssd));\r
1246     ssd->listbox->generic.column = 0;\r
1247     ssd->listbox->listbox.height = 7;\r
1248     if (!midsession) {\r
1249         ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',\r
1250                                           HELPCTX(session_saved),\r
1251                                           sessionsaver_handler, P(ssd));\r
1252         ssd->loadbutton->generic.column = 1;\r
1253     } else {\r
1254         /* We can't offer the Load button mid-session, as it would allow the\r
1255          * user to load and subsequently save settings they can't see. (And\r
1256          * also change otherwise immutable settings underfoot; that probably\r
1257          * shouldn't be a problem, but.) */\r
1258         ssd->loadbutton = NULL;\r
1259     }\r
1260     /* "Save" button is permitted mid-session. */\r
1261     ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',\r
1262                                       HELPCTX(session_saved),\r
1263                                       sessionsaver_handler, P(ssd));\r
1264     ssd->savebutton->generic.column = 1;\r
1265     if (!midsession) {\r
1266         ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',\r
1267                                          HELPCTX(session_saved),\r
1268                                          sessionsaver_handler, P(ssd));\r
1269         ssd->delbutton->generic.column = 1;\r
1270     } else {\r
1271         /* Disable the Delete button mid-session too, for UI consistency. */\r
1272         ssd->delbutton = NULL;\r
1273     }\r
1274     ctrl_columns(s, 1, 100);\r
1275 \r
1276     s = ctrl_getset(b, "Session", "otheropts", NULL);\r
1277     c = ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,\r
1278                           HELPCTX(session_coe),\r
1279                           dlg_stdradiobutton_handler,\r
1280                           I(offsetof(Config, close_on_exit)),\r
1281                           "Always", I(FORCE_ON),\r
1282                           "Never", I(FORCE_OFF),\r
1283                           "Only on clean exit", I(AUTO), NULL);\r
1284 \r
1285     /*\r
1286      * The Session/Logging panel.\r
1287      */\r
1288     ctrl_settitle(b, "Session/Logging", "Options controlling session logging");\r
1289 \r
1290     s = ctrl_getset(b, "Session/Logging", "main", NULL);\r
1291     /*\r
1292      * The logging buttons change depending on whether SSH packet\r
1293      * logging can sensibly be available.\r
1294      */\r
1295     {\r
1296         char *sshlogname, *sshrawlogname;\r
1297         if ((midsession && protocol == PROT_SSH) ||\r
1298             (!midsession && backend_from_proto(PROT_SSH))) {\r
1299             sshlogname = "SSH packets";\r
1300             sshrawlogname = "SSH packets and raw data";\r
1301         } else {\r
1302             sshlogname = NULL;         /* this will disable both buttons */\r
1303             sshrawlogname = NULL;      /* this will just placate optimisers */\r
1304         }\r
1305         ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,\r
1306                           HELPCTX(logging_main),\r
1307                           loggingbuttons_handler,\r
1308                           I(offsetof(Config, logtype)),\r
1309                           "None", 't', I(LGTYP_NONE),\r
1310                           "Printable output", 'p', I(LGTYP_ASCII),\r
1311                           "All session output", 'l', I(LGTYP_DEBUG),\r
1312                           sshlogname, 's', I(LGTYP_PACKETS),\r
1313                           sshrawlogname, 'r', I(LGTYP_SSHRAW),\r
1314                           NULL);\r
1315     }\r
1316     ctrl_filesel(s, "Log file name:", 'f',\r
1317                  NULL, TRUE, "Select session log file name",\r
1318                  HELPCTX(logging_filename),\r
1319                  dlg_stdfilesel_handler, I(offsetof(Config, logfilename)));\r
1320     ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"\r
1321               " &T for time, and &H for host name)",\r
1322               HELPCTX(logging_filename));\r
1323     ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,\r
1324                       HELPCTX(logging_exists),\r
1325                       dlg_stdradiobutton_handler, I(offsetof(Config,logxfovr)),\r
1326                       "Always overwrite it", I(LGXF_OVR),\r
1327                       "Always append to the end of it", I(LGXF_APN),\r
1328                       "Ask the user every time", I(LGXF_ASK), NULL);\r
1329     ctrl_checkbox(s, "Flush log file frequently", 'u',\r
1330                  HELPCTX(logging_flush),\r
1331                  dlg_stdcheckbox_handler, I(offsetof(Config,logflush)));\r
1332 \r
1333     if ((midsession && protocol == PROT_SSH) ||\r
1334         (!midsession && backend_from_proto(PROT_SSH))) {\r
1335         s = ctrl_getset(b, "Session/Logging", "ssh",\r
1336                         "Options specific to SSH packet logging");\r
1337         ctrl_checkbox(s, "Omit known password fields", 'k',\r
1338                       HELPCTX(logging_ssh_omit_password),\r
1339                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitpass)));\r
1340         ctrl_checkbox(s, "Omit session data", 'd',\r
1341                       HELPCTX(logging_ssh_omit_data),\r
1342                       dlg_stdcheckbox_handler, I(offsetof(Config,logomitdata)));\r
1343     }\r
1344 \r
1345     /*\r
1346      * The Terminal panel.\r
1347      */\r
1348     ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");\r
1349 \r
1350     s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");\r
1351     ctrl_checkbox(s, "Auto wrap mode initially on", 'w',\r
1352                   HELPCTX(terminal_autowrap),\r
1353                   dlg_stdcheckbox_handler, I(offsetof(Config,wrap_mode)));\r
1354     ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',\r
1355                   HELPCTX(terminal_decom),\r
1356                   dlg_stdcheckbox_handler, I(offsetof(Config,dec_om)));\r
1357     ctrl_checkbox(s, "Implicit CR in every LF", 'r',\r
1358                   HELPCTX(terminal_lfhascr),\r
1359                   dlg_stdcheckbox_handler, I(offsetof(Config,lfhascr)));\r
1360     ctrl_checkbox(s, "Implicit LF in every CR", 'f',\r
1361                   HELPCTX(terminal_crhaslf),\r
1362                   dlg_stdcheckbox_handler, I(offsetof(Config,crhaslf)));\r
1363     ctrl_checkbox(s, "Use background colour to erase screen", 'e',\r
1364                   HELPCTX(terminal_bce),\r
1365                   dlg_stdcheckbox_handler, I(offsetof(Config,bce)));\r
1366     ctrl_checkbox(s, "Enable blinking text", 'n',\r
1367                   HELPCTX(terminal_blink),\r
1368                   dlg_stdcheckbox_handler, I(offsetof(Config,blinktext)));\r
1369     ctrl_editbox(s, "Answerback to ^E:", 's', 100,\r
1370                  HELPCTX(terminal_answerback),\r
1371                  dlg_stdeditbox_handler, I(offsetof(Config,answerback)),\r
1372                  I(sizeof(((Config *)0)->answerback)));\r
1373 \r
1374     s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");\r
1375     ctrl_radiobuttons(s, "Local echo:", 'l', 3,\r
1376                       HELPCTX(terminal_localecho),\r
1377                       dlg_stdradiobutton_handler,I(offsetof(Config,localecho)),\r
1378                       "Auto", I(AUTO),\r
1379                       "Force on", I(FORCE_ON),\r
1380                       "Force off", I(FORCE_OFF), NULL);\r
1381     ctrl_radiobuttons(s, "Local line editing:", 't', 3,\r
1382                       HELPCTX(terminal_localedit),\r
1383                       dlg_stdradiobutton_handler,I(offsetof(Config,localedit)),\r
1384                       "Auto", I(AUTO),\r
1385                       "Force on", I(FORCE_ON),\r
1386                       "Force off", I(FORCE_OFF), NULL);\r
1387 \r
1388     s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");\r
1389     ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,\r
1390                   HELPCTX(terminal_printing),\r
1391                   printerbox_handler, P(NULL), P(NULL));\r
1392 \r
1393     /*\r
1394      * The Terminal/Keyboard panel.\r
1395      */\r
1396     ctrl_settitle(b, "Terminal/Keyboard",\r
1397                   "Options controlling the effects of keys");\r
1398 \r
1399     s = ctrl_getset(b, "Terminal/Keyboard", "mappings",\r
1400                     "Change the sequences sent by:");\r
1401     ctrl_radiobuttons(s, "The Backspace key", 'b', 2,\r
1402                       HELPCTX(keyboard_backspace),\r
1403                       dlg_stdradiobutton_handler,\r
1404                       I(offsetof(Config, bksp_is_delete)),\r
1405                       "Control-H", I(0), "Control-? (127)", I(1), NULL);\r
1406     ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,\r
1407                       HELPCTX(keyboard_homeend),\r
1408                       dlg_stdradiobutton_handler,\r
1409                       I(offsetof(Config, rxvt_homeend)),\r
1410                       "Standard", I(0), "rxvt", I(1), NULL);\r
1411     ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,\r
1412                       HELPCTX(keyboard_funkeys),\r
1413                       dlg_stdradiobutton_handler,\r
1414                       I(offsetof(Config, funky_type)),\r
1415                       "ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),\r
1416                       "VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);\r
1417 \r
1418     s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",\r
1419                     "Application keypad settings:");\r
1420     ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,\r
1421                       HELPCTX(keyboard_appcursor),\r
1422                       dlg_stdradiobutton_handler,\r
1423                       I(offsetof(Config, app_cursor)),\r
1424                       "Normal", I(0), "Application", I(1), NULL);\r
1425     ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,\r
1426                       HELPCTX(keyboard_appkeypad),\r
1427                       numeric_keypad_handler, P(NULL),\r
1428                       "Normal", I(0), "Application", I(1), "NetHack", I(2),\r
1429                       NULL);\r
1430 \r
1431     /*\r
1432      * The Terminal/Bell panel.\r
1433      */\r
1434     ctrl_settitle(b, "Terminal/Bell",\r
1435                   "Options controlling the terminal bell");\r
1436 \r
1437     s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");\r
1438     ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,\r
1439                       HELPCTX(bell_style),\r
1440                       dlg_stdradiobutton_handler, I(offsetof(Config, beep)),\r
1441                       "None (bell disabled)", I(BELL_DISABLED),\r
1442                       "Make default system alert sound", I(BELL_DEFAULT),\r
1443                       "Visual bell (flash window)", I(BELL_VISUAL), NULL);\r
1444 \r
1445     s = ctrl_getset(b, "Terminal/Bell", "overload",\r
1446                     "Control the bell overload behaviour");\r
1447     ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',\r
1448                   HELPCTX(bell_overload),\r
1449                   dlg_stdcheckbox_handler, I(offsetof(Config,bellovl)));\r
1450     ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,\r
1451                  HELPCTX(bell_overload),\r
1452                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_n)), I(-1));\r
1453     ctrl_editbox(s, "... in this many seconds", 't', 20,\r
1454                  HELPCTX(bell_overload),\r
1455                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_t)),\r
1456                  I(-TICKSPERSEC));\r
1457     ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",\r
1458               HELPCTX(bell_overload));\r
1459     ctrl_editbox(s, "Seconds of silence required", 's', 20,\r
1460                  HELPCTX(bell_overload),\r
1461                  dlg_stdeditbox_handler, I(offsetof(Config,bellovl_s)),\r
1462                  I(-TICKSPERSEC));\r
1463 \r
1464     /*\r
1465      * The Terminal/Features panel.\r
1466      */\r
1467     ctrl_settitle(b, "Terminal/Features",\r
1468                   "Enabling and disabling advanced terminal features");\r
1469 \r
1470     s = ctrl_getset(b, "Terminal/Features", "main", NULL);\r
1471     ctrl_checkbox(s, "Disable application cursor keys mode", 'u',\r
1472                   HELPCTX(features_application),\r
1473                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_c)));\r
1474     ctrl_checkbox(s, "Disable application keypad mode", 'k',\r
1475                   HELPCTX(features_application),\r
1476                   dlg_stdcheckbox_handler, I(offsetof(Config,no_applic_k)));\r
1477     ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',\r
1478                   HELPCTX(features_mouse),\r
1479                   dlg_stdcheckbox_handler, I(offsetof(Config,no_mouse_rep)));\r
1480     ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',\r
1481                   HELPCTX(features_resize),\r
1482                   dlg_stdcheckbox_handler,\r
1483                   I(offsetof(Config,no_remote_resize)));\r
1484     ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',\r
1485                   HELPCTX(features_altscreen),\r
1486                   dlg_stdcheckbox_handler, I(offsetof(Config,no_alt_screen)));\r
1487     ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',\r
1488                   HELPCTX(features_retitle),\r
1489                   dlg_stdcheckbox_handler,\r
1490                   I(offsetof(Config,no_remote_wintitle)));\r
1491     ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,\r
1492                       HELPCTX(features_qtitle),\r
1493                       dlg_stdradiobutton_handler,\r
1494                       I(offsetof(Config,remote_qtitle_action)),\r
1495                       "None", I(TITLE_NONE),\r
1496                       "Empty string", I(TITLE_EMPTY),\r
1497                       "Window title", I(TITLE_REAL), NULL);\r
1498     ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',\r
1499                   HELPCTX(features_dbackspace),\r
1500                   dlg_stdcheckbox_handler, I(offsetof(Config,no_dbackspace)));\r
1501     ctrl_checkbox(s, "Disable remote-controlled character set configuration",\r
1502                   'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,\r
1503                   I(offsetof(Config,no_remote_charset)));\r
1504     ctrl_checkbox(s, "Disable Arabic text shaping",\r
1505                   'l', HELPCTX(features_arabicshaping), dlg_stdcheckbox_handler,\r
1506                   I(offsetof(Config, arabicshaping)));\r
1507     ctrl_checkbox(s, "Disable bidirectional text display",\r
1508                   'd', HELPCTX(features_bidi), dlg_stdcheckbox_handler,\r
1509                   I(offsetof(Config, bidi)));\r
1510 \r
1511     /*\r
1512      * The Window panel.\r
1513      */\r
1514     str = dupprintf("Options controlling %s's window", appname);\r
1515     ctrl_settitle(b, "Window", str);\r
1516     sfree(str);\r
1517 \r
1518     s = ctrl_getset(b, "Window", "size", "Set the size of the window");\r
1519     ctrl_columns(s, 2, 50, 50);\r
1520     c = ctrl_editbox(s, "Columns", 'm', 100,\r
1521                      HELPCTX(window_size),\r
1522                      dlg_stdeditbox_handler, I(offsetof(Config,width)), I(-1));\r
1523     c->generic.column = 0;\r
1524     c = ctrl_editbox(s, "Rows", 'r', 100,\r
1525                      HELPCTX(window_size),\r
1526                      dlg_stdeditbox_handler, I(offsetof(Config,height)),I(-1));\r
1527     c->generic.column = 1;\r
1528     ctrl_columns(s, 1, 100);\r
1529 \r
1530     s = ctrl_getset(b, "Window", "scrollback",\r
1531                     "Control the scrollback in the window");\r
1532     ctrl_editbox(s, "Lines of scrollback", 's', 50,\r
1533                  HELPCTX(window_scrollback),\r
1534                  dlg_stdeditbox_handler, I(offsetof(Config,savelines)), I(-1));\r
1535     ctrl_checkbox(s, "Display scrollbar", 'd',\r
1536                   HELPCTX(window_scrollback),\r
1537                   dlg_stdcheckbox_handler, I(offsetof(Config,scrollbar)));\r
1538     ctrl_checkbox(s, "Reset scrollback on keypress", 'k',\r
1539                   HELPCTX(window_scrollback),\r
1540                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_key)));\r
1541     ctrl_checkbox(s, "Reset scrollback on display activity", 'p',\r
1542                   HELPCTX(window_scrollback),\r
1543                   dlg_stdcheckbox_handler, I(offsetof(Config,scroll_on_disp)));\r
1544     ctrl_checkbox(s, "Push erased text into scrollback", 'e',\r
1545                   HELPCTX(window_erased),\r
1546                   dlg_stdcheckbox_handler,\r
1547                   I(offsetof(Config,erase_to_scrollback)));\r
1548 \r
1549     /*\r
1550      * The Window/Appearance panel.\r
1551      */\r
1552     str = dupprintf("Configure the appearance of %s's window", appname);\r
1553     ctrl_settitle(b, "Window/Appearance", str);\r
1554     sfree(str);\r
1555 \r
1556     s = ctrl_getset(b, "Window/Appearance", "cursor",\r
1557                     "Adjust the use of the cursor");\r
1558     ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,\r
1559                       HELPCTX(appearance_cursor),\r
1560                       dlg_stdradiobutton_handler,\r
1561                       I(offsetof(Config, cursor_type)),\r
1562                       "Block", 'l', I(0),\r
1563                       "Underline", 'u', I(1),\r
1564                       "Vertical line", 'v', I(2), NULL);\r
1565     ctrl_checkbox(s, "Cursor blinks", 'b',\r
1566                   HELPCTX(appearance_cursor),\r
1567                   dlg_stdcheckbox_handler, I(offsetof(Config,blink_cur)));\r
1568 \r
1569     s = ctrl_getset(b, "Window/Appearance", "font",\r
1570                     "Font settings");\r
1571     ctrl_fontsel(s, "Font used in the terminal window", 'n',\r
1572                  HELPCTX(appearance_font),\r
1573                  dlg_stdfontsel_handler, I(offsetof(Config, font)));\r
1574 \r
1575     s = ctrl_getset(b, "Window/Appearance", "mouse",\r
1576                     "Adjust the use of the mouse pointer");\r
1577     ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',\r
1578                   HELPCTX(appearance_hidemouse),\r
1579                   dlg_stdcheckbox_handler, I(offsetof(Config,hide_mouseptr)));\r
1580 \r
1581     s = ctrl_getset(b, "Window/Appearance", "border",\r
1582                     "Adjust the window border");\r
1583     ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,\r
1584                  HELPCTX(appearance_border),\r
1585                  dlg_stdeditbox_handler,\r
1586                  I(offsetof(Config,window_border)), I(-1));\r
1587 \r
1588     /*\r
1589      * The Window/Behaviour panel.\r
1590      */\r
1591     str = dupprintf("Configure the behaviour of %s's window", appname);\r
1592     ctrl_settitle(b, "Window/Behaviour", str);\r
1593     sfree(str);\r
1594 \r
1595     s = ctrl_getset(b, "Window/Behaviour", "title",\r
1596                     "Adjust the behaviour of the window title");\r
1597     ctrl_editbox(s, "Window title:", 't', 100,\r
1598                  HELPCTX(appearance_title),\r
1599                  dlg_stdeditbox_handler, I(offsetof(Config,wintitle)),\r
1600                  I(sizeof(((Config *)0)->wintitle)));\r
1601     ctrl_checkbox(s, "Separate window and icon titles", 'i',\r
1602                   HELPCTX(appearance_title),\r
1603                   dlg_stdcheckbox_handler,\r
1604                   I(CHECKBOX_INVERT | offsetof(Config,win_name_always)));\r
1605 \r
1606     s = ctrl_getset(b, "Window/Behaviour", "main", NULL);\r
1607     ctrl_checkbox(s, "Warn before closing window", 'w',\r
1608                   HELPCTX(behaviour_closewarn),\r
1609                   dlg_stdcheckbox_handler, I(offsetof(Config,warn_on_close)));\r
1610 \r
1611     /*\r
1612      * The Window/Translation panel.\r
1613      */\r
1614     ctrl_settitle(b, "Window/Translation",\r
1615                   "Options controlling character set translation");\r
1616 \r
1617     s = ctrl_getset(b, "Window/Translation", "trans",\r
1618                     "Character set translation");\r
1619     ctrl_combobox(s, "Remote character set:",\r
1620                   'r', 100, HELPCTX(translation_codepage),\r
1621                   codepage_handler, P(NULL), P(NULL));\r
1622 \r
1623     s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);\r
1624     ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',\r
1625                   HELPCTX(translation_cjk_ambig_wide),\r
1626                   dlg_stdcheckbox_handler, I(offsetof(Config,cjk_ambig_wide)));\r
1627 \r
1628     str = dupprintf("Adjust how %s handles line drawing characters", appname);\r
1629     s = ctrl_getset(b, "Window/Translation", "linedraw", str);\r
1630     sfree(str);\r
1631     ctrl_radiobuttons(s, "Handling of line drawing characters:", NO_SHORTCUT,1,\r
1632                       HELPCTX(translation_linedraw),\r
1633                       dlg_stdradiobutton_handler,\r
1634                       I(offsetof(Config, vtmode)),\r
1635                       "Use Unicode line drawing code points",'u',I(VT_UNICODE),\r
1636                       "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN),\r
1637                       NULL);\r
1638     ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',\r
1639                   HELPCTX(selection_linedraw),\r
1640                   dlg_stdcheckbox_handler, I(offsetof(Config,rawcnp)));\r
1641 \r
1642     /*\r
1643      * The Window/Selection panel.\r
1644      */\r
1645     ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");\r
1646         \r
1647     s = ctrl_getset(b, "Window/Selection", "mouse",\r
1648                     "Control use of mouse");\r
1649     ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',\r
1650                   HELPCTX(selection_shiftdrag),\r
1651                   dlg_stdcheckbox_handler, I(offsetof(Config,mouse_override)));\r
1652     ctrl_radiobuttons(s,\r
1653                       "Default selection mode (Alt+drag does the other one):",\r
1654                       NO_SHORTCUT, 2,\r
1655                       HELPCTX(selection_rect),\r
1656                       dlg_stdradiobutton_handler,\r
1657                       I(offsetof(Config, rect_select)),\r
1658                       "Normal", 'n', I(0),\r
1659                       "Rectangular block", 'r', I(1), NULL);\r
1660 \r
1661     s = ctrl_getset(b, "Window/Selection", "charclass",\r
1662                     "Control the select-one-word-at-a-time mode");\r
1663     ccd = (struct charclass_data *)\r
1664         ctrl_alloc(b, sizeof(struct charclass_data));\r
1665     ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',\r
1666                                 HELPCTX(selection_charclasses),\r
1667                                 charclass_handler, P(ccd));\r
1668     ccd->listbox->listbox.multisel = 1;\r
1669     ccd->listbox->listbox.ncols = 4;\r
1670     ccd->listbox->listbox.percentages = snewn(4, int);\r
1671     ccd->listbox->listbox.percentages[0] = 15;\r
1672     ccd->listbox->listbox.percentages[1] = 25;\r
1673     ccd->listbox->listbox.percentages[2] = 20;\r
1674     ccd->listbox->listbox.percentages[3] = 40;\r
1675     ctrl_columns(s, 2, 67, 33);\r
1676     ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,\r
1677                                 HELPCTX(selection_charclasses),\r
1678                                 charclass_handler, P(ccd), P(NULL));\r
1679     ccd->editbox->generic.column = 0;\r
1680     ccd->button = ctrl_pushbutton(s, "Set", 's',\r
1681                                   HELPCTX(selection_charclasses),\r
1682                                   charclass_handler, P(ccd));\r
1683     ccd->button->generic.column = 1;\r
1684     ctrl_columns(s, 1, 100);\r
1685 \r
1686     /*\r
1687      * The Window/Colours panel.\r
1688      */\r
1689     ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");\r
1690 \r
1691     s = ctrl_getset(b, "Window/Colours", "general",\r
1692                     "General options for colour usage");\r
1693     ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',\r
1694                   HELPCTX(colours_ansi),\r
1695                   dlg_stdcheckbox_handler, I(offsetof(Config,ansi_colour)));\r
1696     ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',\r
1697                   HELPCTX(colours_xterm256), dlg_stdcheckbox_handler,\r
1698                   I(offsetof(Config,xterm_256_colour)));\r
1699     ctrl_checkbox(s, "Bolded text is a different colour", 'b',\r
1700                   HELPCTX(colours_bold),\r
1701                   dlg_stdcheckbox_handler, I(offsetof(Config,bold_colour)));\r
1702 \r
1703     str = dupprintf("Adjust the precise colours %s displays", appname);\r
1704     s = ctrl_getset(b, "Window/Colours", "adjust", str);\r
1705     sfree(str);\r
1706     ctrl_text(s, "Select a colour from the list, and then click the"\r
1707               " Modify button to change its appearance.",\r
1708               HELPCTX(colours_config));\r
1709     ctrl_columns(s, 2, 67, 33);\r
1710     cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));\r
1711     cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',\r
1712                                HELPCTX(colours_config), colour_handler, P(cd));\r
1713     cd->listbox->generic.column = 0;\r
1714     cd->listbox->listbox.height = 7;\r
1715     c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));\r
1716     c->generic.column = 1;\r
1717     cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),\r
1718                              colour_handler, P(cd), P(NULL));\r
1719     cd->redit->generic.column = 1;\r
1720     cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),\r
1721                              colour_handler, P(cd), P(NULL));\r
1722     cd->gedit->generic.column = 1;\r
1723     cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),\r
1724                              colour_handler, P(cd), P(NULL));\r
1725     cd->bedit->generic.column = 1;\r
1726     cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),\r
1727                                  colour_handler, P(cd));\r
1728     cd->button->generic.column = 1;\r
1729     ctrl_columns(s, 1, 100);\r
1730 \r
1731     /*\r
1732      * The Connection panel. This doesn't show up if we're in a\r
1733      * non-network utility such as pterm. We tell this by being\r
1734      * passed a protocol < 0.\r
1735      */\r
1736     if (protocol >= 0) {\r
1737         ctrl_settitle(b, "Connection", "Options controlling the connection");\r
1738 \r
1739         s = ctrl_getset(b, "Connection", "keepalive",\r
1740                         "Sending of null packets to keep session active");\r
1741         ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,\r
1742                      HELPCTX(connection_keepalive),\r
1743                      dlg_stdeditbox_handler, I(offsetof(Config,ping_interval)),\r
1744                      I(-1));\r
1745 \r
1746         if (!midsession) {\r
1747             s = ctrl_getset(b, "Connection", "tcp",\r
1748                             "Low-level TCP connection options");\r
1749             ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",\r
1750                           'n', HELPCTX(connection_nodelay),\r
1751                           dlg_stdcheckbox_handler,\r
1752                           I(offsetof(Config,tcp_nodelay)));\r
1753             ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",\r
1754                           'p', HELPCTX(connection_tcpkeepalive),\r
1755                           dlg_stdcheckbox_handler,\r
1756                           I(offsetof(Config,tcp_keepalives)));\r
1757 #ifndef NO_IPV6\r
1758             s = ctrl_getset(b, "Connection", "ipversion",\r
1759                           "Internet protocol version");\r
1760             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,\r
1761                           HELPCTX(connection_ipversion),\r
1762                           dlg_stdradiobutton_handler,\r
1763                           I(offsetof(Config, addressfamily)),\r
1764                           "Auto", 'u', I(ADDRTYPE_UNSPEC),\r
1765                           "IPv4", '4', I(ADDRTYPE_IPV4),\r
1766                           "IPv6", '6', I(ADDRTYPE_IPV6),\r
1767                           NULL);\r
1768 #endif\r
1769 \r
1770             {\r
1771                 char *label = backend_from_proto(PROT_SSH) ?\r
1772                     "Logical name of remote host (e.g. for SSH key lookup):" :\r
1773                     "Logical name of remote host:";\r
1774                 s = ctrl_getset(b, "Connection", "identity",\r
1775                                 "Logical name of remote host");\r
1776                 ctrl_editbox(s, label, 'm', 100,\r
1777                              HELPCTX(connection_loghost),\r
1778                              dlg_stdeditbox_handler, I(offsetof(Config,loghost)),\r
1779                              I(sizeof(((Config *)0)->loghost)));\r
1780             }\r
1781         }\r
1782 \r
1783         /*\r
1784          * A sub-panel Connection/Data, containing options that\r
1785          * decide on data to send to the server.\r
1786          */\r
1787         if (!midsession) {\r
1788             ctrl_settitle(b, "Connection/Data", "Data to send to the server");\r
1789 \r
1790             s = ctrl_getset(b, "Connection/Data", "login",\r
1791                             "Login details");\r
1792             ctrl_editbox(s, "Auto-login username", 'u', 50,\r
1793                          HELPCTX(connection_username),\r
1794                          dlg_stdeditbox_handler, I(offsetof(Config,username)),\r
1795                          I(sizeof(((Config *)0)->username)));\r
1796             {\r
1797                 /* We assume the local username is sufficiently stable\r
1798                  * to include on the dialog box. */\r
1799                 char *user = get_username();\r
1800                 char *userlabel = dupprintf("Use system username (%s)",\r
1801                                             user ? user : "");\r
1802                 sfree(user);\r
1803                 ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,\r
1804                                   HELPCTX(connection_username_from_env),\r
1805                                   dlg_stdradiobutton_handler,\r
1806                                   I(offsetof(Config, username_from_env)),\r
1807                                   "Prompt", I(FALSE),\r
1808                                   userlabel, I(TRUE),\r
1809                                   NULL);\r
1810                 sfree(userlabel);\r
1811             }\r
1812 \r
1813             s = ctrl_getset(b, "Connection/Data", "term",\r
1814                             "Terminal details");\r
1815             ctrl_editbox(s, "Terminal-type string", 't', 50,\r
1816                          HELPCTX(connection_termtype),\r
1817                          dlg_stdeditbox_handler, I(offsetof(Config,termtype)),\r
1818                          I(sizeof(((Config *)0)->termtype)));\r
1819             ctrl_editbox(s, "Terminal speeds", 's', 50,\r
1820                          HELPCTX(connection_termspeed),\r
1821                          dlg_stdeditbox_handler, I(offsetof(Config,termspeed)),\r
1822                          I(sizeof(((Config *)0)->termspeed)));\r
1823 \r
1824             s = ctrl_getset(b, "Connection/Data", "env",\r
1825                             "Environment variables");\r
1826             ctrl_columns(s, 2, 80, 20);\r
1827             ed = (struct environ_data *)\r
1828                 ctrl_alloc(b, sizeof(struct environ_data));\r
1829             ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,\r
1830                                       HELPCTX(telnet_environ),\r
1831                                       environ_handler, P(ed), P(NULL));\r
1832             ed->varbox->generic.column = 0;\r
1833             ed->valbox = ctrl_editbox(s, "Value", 'l', 60,\r
1834                                       HELPCTX(telnet_environ),\r
1835                                       environ_handler, P(ed), P(NULL));\r
1836             ed->valbox->generic.column = 0;\r
1837             ed->addbutton = ctrl_pushbutton(s, "Add", 'd',\r
1838                                             HELPCTX(telnet_environ),\r
1839                                             environ_handler, P(ed));\r
1840             ed->addbutton->generic.column = 1;\r
1841             ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',\r
1842                                             HELPCTX(telnet_environ),\r
1843                                             environ_handler, P(ed));\r
1844             ed->rembutton->generic.column = 1;\r
1845             ctrl_columns(s, 1, 100);\r
1846             ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
1847                                        HELPCTX(telnet_environ),\r
1848                                        environ_handler, P(ed));\r
1849             ed->listbox->listbox.height = 3;\r
1850             ed->listbox->listbox.ncols = 2;\r
1851             ed->listbox->listbox.percentages = snewn(2, int);\r
1852             ed->listbox->listbox.percentages[0] = 30;\r
1853             ed->listbox->listbox.percentages[1] = 70;\r
1854         }\r
1855 \r
1856     }\r
1857 \r
1858     if (!midsession) {\r
1859         /*\r
1860          * The Connection/Proxy panel.\r
1861          */\r
1862         ctrl_settitle(b, "Connection/Proxy",\r
1863                       "Options controlling proxy usage");\r
1864 \r
1865         s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);\r
1866         ctrl_radiobuttons(s, "Proxy type:", 't', 3,\r
1867                           HELPCTX(proxy_type),\r
1868                           dlg_stdradiobutton_handler,\r
1869                           I(offsetof(Config, proxy_type)),\r
1870                           "None", I(PROXY_NONE),\r
1871                           "SOCKS 4", I(PROXY_SOCKS4),\r
1872                           "SOCKS 5", I(PROXY_SOCKS5),\r
1873                           "HTTP", I(PROXY_HTTP),\r
1874                           "Telnet", I(PROXY_TELNET),\r
1875                           NULL);\r
1876         ctrl_columns(s, 2, 80, 20);\r
1877         c = ctrl_editbox(s, "Proxy hostname", 'y', 100,\r
1878                          HELPCTX(proxy_main),\r
1879                          dlg_stdeditbox_handler,\r
1880                          I(offsetof(Config,proxy_host)),\r
1881                          I(sizeof(((Config *)0)->proxy_host)));\r
1882         c->generic.column = 0;\r
1883         c = ctrl_editbox(s, "Port", 'p', 100,\r
1884                          HELPCTX(proxy_main),\r
1885                          dlg_stdeditbox_handler,\r
1886                          I(offsetof(Config,proxy_port)),\r
1887                          I(-1));\r
1888         c->generic.column = 1;\r
1889         ctrl_columns(s, 1, 100);\r
1890         ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,\r
1891                      HELPCTX(proxy_exclude),\r
1892                      dlg_stdeditbox_handler,\r
1893                      I(offsetof(Config,proxy_exclude_list)),\r
1894                      I(sizeof(((Config *)0)->proxy_exclude_list)));\r
1895         ctrl_checkbox(s, "Consider proxying local host connections", 'x',\r
1896                       HELPCTX(proxy_exclude),\r
1897                       dlg_stdcheckbox_handler,\r
1898                       I(offsetof(Config,even_proxy_localhost)));\r
1899         ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,\r
1900                           HELPCTX(proxy_dns),\r
1901                           dlg_stdradiobutton_handler,\r
1902                           I(offsetof(Config, proxy_dns)),\r
1903                           "No", I(FORCE_OFF),\r
1904                           "Auto", I(AUTO),\r
1905                           "Yes", I(FORCE_ON), NULL);\r
1906         ctrl_editbox(s, "Username", 'u', 60,\r
1907                      HELPCTX(proxy_auth),\r
1908                      dlg_stdeditbox_handler,\r
1909                      I(offsetof(Config,proxy_username)),\r
1910                      I(sizeof(((Config *)0)->proxy_username)));\r
1911         c = ctrl_editbox(s, "Password", 'w', 60,\r
1912                          HELPCTX(proxy_auth),\r
1913                          dlg_stdeditbox_handler,\r
1914                          I(offsetof(Config,proxy_password)),\r
1915                          I(sizeof(((Config *)0)->proxy_password)));\r
1916         c->editbox.password = 1;\r
1917         ctrl_editbox(s, "Telnet command", 'm', 100,\r
1918                      HELPCTX(proxy_command),\r
1919                      dlg_stdeditbox_handler,\r
1920                      I(offsetof(Config,proxy_telnet_command)),\r
1921                      I(sizeof(((Config *)0)->proxy_telnet_command)));\r
1922     }\r
1923 \r
1924     /*\r
1925      * The Telnet panel exists in the base config box, and in a\r
1926      * mid-session reconfig box _if_ we're using Telnet.\r
1927      */\r
1928     if (!midsession || protocol == PROT_TELNET) {\r
1929         /*\r
1930          * The Connection/Telnet panel.\r
1931          */\r
1932         ctrl_settitle(b, "Connection/Telnet",\r
1933                       "Options controlling Telnet connections");\r
1934 \r
1935         s = ctrl_getset(b, "Connection/Telnet", "protocol",\r
1936                         "Telnet protocol adjustments");\r
1937 \r
1938         if (!midsession) {\r
1939             ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",\r
1940                               NO_SHORTCUT, 2,\r
1941                               HELPCTX(telnet_oldenviron),\r
1942                               dlg_stdradiobutton_handler,\r
1943                               I(offsetof(Config, rfc_environ)),\r
1944                               "BSD (commonplace)", 'b', I(0),\r
1945                               "RFC 1408 (unusual)", 'f', I(1), NULL);\r
1946             ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,\r
1947                               HELPCTX(telnet_passive),\r
1948                               dlg_stdradiobutton_handler,\r
1949                               I(offsetof(Config, passive_telnet)),\r
1950                               "Passive", I(1), "Active", I(0), NULL);\r
1951         }\r
1952         ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',\r
1953                       HELPCTX(telnet_specialkeys),\r
1954                       dlg_stdcheckbox_handler,\r
1955                       I(offsetof(Config,telnet_keyboard)));\r
1956         ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",\r
1957                       'm', HELPCTX(telnet_newline),\r
1958                       dlg_stdcheckbox_handler,\r
1959                       I(offsetof(Config,telnet_newline)));\r
1960     }\r
1961 \r
1962     if (!midsession) {\r
1963 \r
1964         /*\r
1965          * The Connection/Rlogin panel.\r
1966          */\r
1967         ctrl_settitle(b, "Connection/Rlogin",\r
1968                       "Options controlling Rlogin connections");\r
1969 \r
1970         s = ctrl_getset(b, "Connection/Rlogin", "data",\r
1971                         "Data to send to the server");\r
1972         ctrl_editbox(s, "Local username:", 'l', 50,\r
1973                      HELPCTX(rlogin_localuser),\r
1974                      dlg_stdeditbox_handler, I(offsetof(Config,localusername)),\r
1975                      I(sizeof(((Config *)0)->localusername)));\r
1976 \r
1977     }\r
1978 \r
1979     /*\r
1980      * All the SSH stuff is omitted in PuTTYtel, or in a reconfig\r
1981      * when we're not doing SSH.\r
1982      */\r
1983 \r
1984     if (backend_from_proto(PROT_SSH) && (!midsession || protocol == PROT_SSH)) {\r
1985 \r
1986         /*\r
1987          * The Connection/SSH panel.\r
1988          */\r
1989         ctrl_settitle(b, "Connection/SSH",\r
1990                       "Options controlling SSH connections");\r
1991 \r
1992         if (midsession && protcfginfo == 1) {\r
1993             s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);\r
1994             ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"\r
1995                       "session; it is only here so that sub-panels of it can "\r
1996                       "exist without looking strange.", HELPCTX(no_help));\r
1997         }\r
1998 \r
1999         if (!midsession) {\r
2000 \r
2001             s = ctrl_getset(b, "Connection/SSH", "data",\r
2002                             "Data to send to the server");\r
2003             ctrl_editbox(s, "Remote command:", 'r', 100,\r
2004                          HELPCTX(ssh_command),\r
2005                          dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),\r
2006                          I(sizeof(((Config *)0)->remote_cmd)));\r
2007 \r
2008             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");\r
2009             ctrl_checkbox(s, "Don't start a shell or command at all", 'n',\r
2010                           HELPCTX(ssh_noshell),\r
2011                           dlg_stdcheckbox_handler,\r
2012                           I(offsetof(Config,ssh_no_shell)));\r
2013         }\r
2014 \r
2015         if (!midsession || protcfginfo != 1) {\r
2016             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");\r
2017 \r
2018             ctrl_checkbox(s, "Enable compression", 'e',\r
2019                           HELPCTX(ssh_compress),\r
2020                           dlg_stdcheckbox_handler,\r
2021                           I(offsetof(Config,compression)));\r
2022         }\r
2023 \r
2024         if (!midsession) {\r
2025             s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");\r
2026 \r
2027             ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,\r
2028                               HELPCTX(ssh_protocol),\r
2029                               dlg_stdradiobutton_handler,\r
2030                               I(offsetof(Config, sshprot)),\r
2031                               "1 only", 'l', I(0),\r
2032                               "1", '1', I(1),\r
2033                               "2", '2', I(2),\r
2034                               "2 only", 'y', I(3), NULL);\r
2035         }\r
2036 \r
2037         if (!midsession || protcfginfo != 1) {\r
2038             s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");\r
2039             c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',\r
2040                               HELPCTX(ssh_ciphers),\r
2041                               cipherlist_handler, P(NULL));\r
2042             c->listbox.height = 6;\r
2043 \r
2044             ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',\r
2045                           HELPCTX(ssh_ciphers),\r
2046                           dlg_stdcheckbox_handler,\r
2047                           I(offsetof(Config,ssh2_des_cbc)));\r
2048         }\r
2049 \r
2050         /*\r
2051          * The Connection/SSH/Kex panel. (Owing to repeat key\r
2052          * exchange, this is all meaningful in mid-session _if_\r
2053          * we're using SSH-2 or haven't decided yet.)\r
2054          */\r
2055         if (protcfginfo != 1) {\r
2056             ctrl_settitle(b, "Connection/SSH/Kex",\r
2057                           "Options controlling SSH key exchange");\r
2058 \r
2059             s = ctrl_getset(b, "Connection/SSH/Kex", "main",\r
2060                             "Key exchange algorithm options");\r
2061             c = ctrl_draglist(s, "Algorithm selection policy:", 's',\r
2062                               HELPCTX(ssh_kexlist),\r
2063                               kexlist_handler, P(NULL));\r
2064             c->listbox.height = 5;\r
2065 \r
2066             s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",\r
2067                             "Options controlling key re-exchange");\r
2068 \r
2069             ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,\r
2070                          HELPCTX(ssh_kex_repeat),\r
2071                          dlg_stdeditbox_handler,\r
2072                          I(offsetof(Config,ssh_rekey_time)),\r
2073                          I(-1));\r
2074             ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,\r
2075                          HELPCTX(ssh_kex_repeat),\r
2076                          dlg_stdeditbox_handler,\r
2077                          I(offsetof(Config,ssh_rekey_data)),\r
2078                          I(16));\r
2079             ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",\r
2080                       HELPCTX(ssh_kex_repeat));\r
2081         }\r
2082 \r
2083         if (!midsession) {\r
2084 \r
2085             /*\r
2086              * The Connection/SSH/Auth panel.\r
2087              */\r
2088             ctrl_settitle(b, "Connection/SSH/Auth",\r
2089                           "Options controlling SSH authentication");\r
2090 \r
2091             s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);\r
2092             ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',\r
2093                           HELPCTX(ssh_auth_bypass),\r
2094                           dlg_stdcheckbox_handler,\r
2095                           I(offsetof(Config,ssh_no_userauth)));\r
2096             ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",\r
2097                           'd', HELPCTX(ssh_auth_banner),\r
2098                           dlg_stdcheckbox_handler,\r
2099                           I(offsetof(Config,ssh_show_banner)));\r
2100 \r
2101             s = ctrl_getset(b, "Connection/SSH/Auth", "methods",\r
2102                             "Authentication methods");\r
2103             ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',\r
2104                           HELPCTX(ssh_auth_pageant),\r
2105                           dlg_stdcheckbox_handler,\r
2106                           I(offsetof(Config,tryagent)));\r
2107             ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',\r
2108                           HELPCTX(ssh_auth_tis),\r
2109                           dlg_stdcheckbox_handler,\r
2110                           I(offsetof(Config,try_tis_auth)));\r
2111             ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",\r
2112                           'i', HELPCTX(ssh_auth_ki),\r
2113                           dlg_stdcheckbox_handler,\r
2114                           I(offsetof(Config,try_ki_auth)));\r
2115 \r
2116             s = ctrl_getset(b, "Connection/SSH/Auth", "params",\r
2117                             "Authentication parameters");\r
2118             ctrl_checkbox(s, "Allow agent forwarding", 'f',\r
2119                           HELPCTX(ssh_auth_agentfwd),\r
2120                           dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));\r
2121             ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,\r
2122                           HELPCTX(ssh_auth_changeuser),\r
2123                           dlg_stdcheckbox_handler,\r
2124                           I(offsetof(Config,change_username)));\r
2125             ctrl_filesel(s, "Private key file for authentication:", 'k',\r
2126                          FILTER_KEY_FILES, FALSE, "Select private key file",\r
2127                          HELPCTX(ssh_auth_privkey),\r
2128                          dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));\r
2129 \r
2130 #ifndef NO_GSSAPI\r
2131             /*\r
2132              * Connection/SSH/Auth/GSSAPI, which sadly won't fit on\r
2133              * the main Auth panel.\r
2134              */\r
2135             ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",\r
2136                           "Options controlling GSSAPI authentication");\r
2137             s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);\r
2138 \r
2139             ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",\r
2140                           't', HELPCTX(ssh_gssapi),\r
2141                           dlg_stdcheckbox_handler,\r
2142                           I(offsetof(Config,try_gssapi_auth)));\r
2143 \r
2144             ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',\r
2145                           HELPCTX(ssh_gssapi_delegation),\r
2146                           dlg_stdcheckbox_handler,\r
2147                           I(offsetof(Config,gssapifwd)));\r
2148 \r
2149             /*\r
2150              * GSSAPI library selection.\r
2151              */\r
2152             if (ngsslibs > 1) {\r
2153                 c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",\r
2154                                   'p', HELPCTX(ssh_gssapi_libraries),\r
2155                                   gsslist_handler, P(NULL));\r
2156                 c->listbox.height = ngsslibs;\r
2157 \r
2158                 /*\r
2159                  * I currently assume that if more than one GSS\r
2160                  * library option is available, then one of them is\r
2161                  * 'user-supplied' and so we should present the\r
2162                  * following file selector. This is at least half-\r
2163                  * reasonable, because if we're using statically\r
2164                  * linked GSSAPI then there will only be one option\r
2165                  * and no way to load from a user-supplied library,\r
2166                  * whereas if we're using dynamic libraries then\r
2167                  * there will almost certainly be some default\r
2168                  * option in addition to a user-supplied path. If\r
2169                  * anyone ever ports PuTTY to a system on which\r
2170                  * dynamic-library GSSAPI is available but there is\r
2171                  * absolutely no consensus on where to keep the\r
2172                  * libraries, there'll need to be a flag alongside\r
2173                  * ngsslibs to control whether the file selector is\r
2174                  * displayed. \r
2175                  */\r
2176 \r
2177                 ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',\r
2178                              FILTER_DYNLIB_FILES, FALSE, "Select library file",\r
2179                              HELPCTX(ssh_gssapi_libraries),\r
2180                              dlg_stdfilesel_handler,\r
2181                              I(offsetof(Config, ssh_gss_custom)));\r
2182             }\r
2183 #endif\r
2184         }\r
2185 \r
2186         if (!midsession) {\r
2187             /*\r
2188              * The Connection/SSH/TTY panel.\r
2189              */\r
2190             ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");\r
2191 \r
2192             s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);\r
2193             ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',\r
2194                           HELPCTX(ssh_nopty),\r
2195                           dlg_stdcheckbox_handler,\r
2196                           I(offsetof(Config,nopty)));\r
2197 \r
2198             s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",\r
2199                             "Terminal modes");\r
2200             td = (struct ttymodes_data *)\r
2201                 ctrl_alloc(b, sizeof(struct ttymodes_data));\r
2202             ctrl_columns(s, 2, 75, 25);\r
2203             c = ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));\r
2204             c->generic.column = 0;\r
2205             td->rembutton = ctrl_pushbutton(s, "Remove", 'r',\r
2206                                             HELPCTX(ssh_ttymodes),\r
2207                                             ttymodes_handler, P(td));\r
2208             td->rembutton->generic.column = 1;\r
2209             td->rembutton->generic.tabdelay = 1;\r
2210             ctrl_columns(s, 1, 100);\r
2211             td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
2212                                        HELPCTX(ssh_ttymodes),\r
2213                                        ttymodes_handler, P(td));\r
2214             td->listbox->listbox.multisel = 1;\r
2215             td->listbox->listbox.height = 4;\r
2216             td->listbox->listbox.ncols = 2;\r
2217             td->listbox->listbox.percentages = snewn(2, int);\r
2218             td->listbox->listbox.percentages[0] = 40;\r
2219             td->listbox->listbox.percentages[1] = 60;\r
2220             ctrl_tabdelay(s, td->rembutton);\r
2221             ctrl_columns(s, 2, 75, 25);\r
2222             td->modelist = ctrl_droplist(s, "Mode:", 'm', 67,\r
2223                                          HELPCTX(ssh_ttymodes),\r
2224                                          ttymodes_handler, P(td));\r
2225             td->modelist->generic.column = 0;\r
2226             td->addbutton = ctrl_pushbutton(s, "Add", 'd',\r
2227                                             HELPCTX(ssh_ttymodes),\r
2228                                             ttymodes_handler, P(td));\r
2229             td->addbutton->generic.column = 1;\r
2230             td->addbutton->generic.tabdelay = 1;\r
2231             ctrl_columns(s, 1, 100);        /* column break */\r
2232             /* Bit of a hack to get the value radio buttons and\r
2233              * edit-box on the same row. */\r
2234             ctrl_columns(s, 3, 25, 50, 25);\r
2235             c = ctrl_text(s, "Value:", HELPCTX(ssh_ttymodes));\r
2236             c->generic.column = 0;\r
2237             td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2,\r
2238                                              HELPCTX(ssh_ttymodes),\r
2239                                              ttymodes_handler, P(td),\r
2240                                              "Auto", NO_SHORTCUT, P(NULL),\r
2241                                              "This:", NO_SHORTCUT, P(NULL),\r
2242                                              NULL);\r
2243             td->valradio->generic.column = 1;\r
2244             td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,\r
2245                                       HELPCTX(ssh_ttymodes),\r
2246                                       ttymodes_handler, P(td), P(NULL));\r
2247             td->valbox->generic.column = 2;\r
2248             ctrl_tabdelay(s, td->addbutton);\r
2249 \r
2250         }\r
2251 \r
2252         if (!midsession) {\r
2253             /*\r
2254              * The Connection/SSH/X11 panel.\r
2255              */\r
2256             ctrl_settitle(b, "Connection/SSH/X11",\r
2257                           "Options controlling SSH X11 forwarding");\r
2258 \r
2259             s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");\r
2260             ctrl_checkbox(s, "Enable X11 forwarding", 'e',\r
2261                           HELPCTX(ssh_tunnels_x11),\r
2262                           dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));\r
2263             ctrl_editbox(s, "X display location", 'x', 50,\r
2264                          HELPCTX(ssh_tunnels_x11),\r
2265                          dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),\r
2266                          I(sizeof(((Config *)0)->x11_display)));\r
2267             ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,\r
2268                               HELPCTX(ssh_tunnels_x11auth),\r
2269                               dlg_stdradiobutton_handler,\r
2270                               I(offsetof(Config, x11_auth)),\r
2271                               "MIT-Magic-Cookie-1", I(X11_MIT),\r
2272                               "XDM-Authorization-1", I(X11_XDM), NULL);\r
2273         }\r
2274 \r
2275         /*\r
2276          * The Tunnels panel _is_ still available in mid-session.\r
2277          */\r
2278         ctrl_settitle(b, "Connection/SSH/Tunnels",\r
2279                       "Options controlling SSH port forwarding");\r
2280 \r
2281         s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",\r
2282                         "Port forwarding");\r
2283         ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',\r
2284                       HELPCTX(ssh_tunnels_portfwd_localhost),\r
2285                       dlg_stdcheckbox_handler,\r
2286                       I(offsetof(Config,lport_acceptall)));\r
2287         ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',\r
2288                       HELPCTX(ssh_tunnels_portfwd_localhost),\r
2289                       dlg_stdcheckbox_handler,\r
2290                       I(offsetof(Config,rport_acceptall)));\r
2291 \r
2292         ctrl_columns(s, 3, 55, 20, 25);\r
2293         c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));\r
2294         c->generic.column = COLUMN_FIELD(0,2);\r
2295         /* You want to select from the list, _then_ hit Remove. So tab order\r
2296          * should be that way round. */\r
2297         pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));\r
2298         pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',\r
2299                                          HELPCTX(ssh_tunnels_portfwd),\r
2300                                          portfwd_handler, P(pfd));\r
2301         pfd->rembutton->generic.column = 2;\r
2302         pfd->rembutton->generic.tabdelay = 1;\r
2303         pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,\r
2304                                     HELPCTX(ssh_tunnels_portfwd),\r
2305                                     portfwd_handler, P(pfd));\r
2306         pfd->listbox->listbox.height = 3;\r
2307         pfd->listbox->listbox.ncols = 2;\r
2308         pfd->listbox->listbox.percentages = snewn(2, int);\r
2309         pfd->listbox->listbox.percentages[0] = 20;\r
2310         pfd->listbox->listbox.percentages[1] = 80;\r
2311         ctrl_tabdelay(s, pfd->rembutton);\r
2312         ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));\r
2313         /* You want to enter source, destination and type, _then_ hit Add.\r
2314          * Again, we adjust the tab order to reflect this. */\r
2315         pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',\r
2316                                          HELPCTX(ssh_tunnels_portfwd),\r
2317                                          portfwd_handler, P(pfd));\r
2318         pfd->addbutton->generic.column = 2;\r
2319         pfd->addbutton->generic.tabdelay = 1;\r
2320         pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,\r
2321                                       HELPCTX(ssh_tunnels_portfwd),\r
2322                                       portfwd_handler, P(pfd), P(NULL));\r
2323         pfd->sourcebox->generic.column = 0;\r
2324         pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,\r
2325                                     HELPCTX(ssh_tunnels_portfwd),\r
2326                                     portfwd_handler, P(pfd), P(NULL));\r
2327         pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,\r
2328                                            HELPCTX(ssh_tunnels_portfwd),\r
2329                                            portfwd_handler, P(pfd),\r
2330                                            "Local", 'l', P(NULL),\r
2331                                            "Remote", 'm', P(NULL),\r
2332                                            "Dynamic", 'y', P(NULL),\r
2333                                            NULL);\r
2334 #ifndef NO_IPV6\r
2335         pfd->addressfamily =\r
2336             ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,\r
2337                               HELPCTX(ssh_tunnels_portfwd_ipversion),\r
2338                               portfwd_handler, P(pfd),\r
2339                               "Auto", 'u', I(ADDRTYPE_UNSPEC),\r
2340                               "IPv4", '4', I(ADDRTYPE_IPV4),\r
2341                               "IPv6", '6', I(ADDRTYPE_IPV6),\r
2342                               NULL);\r
2343 #endif\r
2344         ctrl_tabdelay(s, pfd->addbutton);\r
2345         ctrl_columns(s, 1, 100);\r
2346 \r
2347         if (!midsession) {\r
2348             /*\r
2349              * The Connection/SSH/Bugs panel.\r
2350              */\r
2351             ctrl_settitle(b, "Connection/SSH/Bugs",\r
2352                           "Workarounds for SSH server bugs");\r
2353 \r
2354             s = ctrl_getset(b, "Connection/SSH/Bugs", "main",\r
2355                             "Detection of known bugs in SSH servers");\r
2356             ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,\r
2357                           HELPCTX(ssh_bugs_ignore1),\r
2358                           sshbug_handler, I(offsetof(Config,sshbug_ignore1)));\r
2359             ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,\r
2360                           HELPCTX(ssh_bugs_plainpw1),\r
2361                           sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));\r
2362             ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,\r
2363                           HELPCTX(ssh_bugs_rsa1),\r
2364                           sshbug_handler, I(offsetof(Config,sshbug_rsa1)));\r
2365             ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,\r
2366                           HELPCTX(ssh_bugs_ignore2),\r
2367                           sshbug_handler, I(offsetof(Config,sshbug_ignore2)));\r
2368             ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,\r
2369                           HELPCTX(ssh_bugs_hmac2),\r
2370                           sshbug_handler, I(offsetof(Config,sshbug_hmac2)));\r
2371             ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,\r
2372                           HELPCTX(ssh_bugs_derivekey2),\r
2373                           sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));\r
2374             ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,\r
2375                           HELPCTX(ssh_bugs_rsapad2),\r
2376                           sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));\r
2377             ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,\r
2378                           HELPCTX(ssh_bugs_pksessid2),\r
2379                           sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));\r
2380             ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,\r
2381                           HELPCTX(ssh_bugs_rekey2),\r
2382                           sshbug_handler, I(offsetof(Config,sshbug_rekey2)));\r
2383             ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,\r
2384                           HELPCTX(ssh_bugs_maxpkt2),\r
2385                           sshbug_handler, I(offsetof(Config,sshbug_maxpkt2)));\r
2386         }\r
2387     }\r
2388 }\r