OSDN Git Service

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