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